diff options
author | Christian Grothoff <grothoff@gnunet.org> | 2023-09-01 14:27:48 +0200 |
---|---|---|
committer | Christian Grothoff <grothoff@gnunet.org> | 2023-09-01 14:27:48 +0200 |
commit | 35dcd4514a93ba0f5353ecd1194fc9b515f2aad4 (patch) | |
tree | 399d8f8fbebf5e8ac383fc318b7be08901279a31 | |
parent | 858e3047b8b595ab693e16ce0bbe0b8983b072ed (diff) |
new CRUD APIs for OTP devices and merchant accounts (fixes #7929, #7824), one minor test is still failing...
118 files changed, 7977 insertions, 2363 deletions
diff --git a/src/backend/#taler-merchant-httpd_private-patch-templates-ID.c# b/src/backend/#taler-merchant-httpd_private-patch-templates-ID.c# deleted file mode 100644 index 6739c7a9..00000000 --- a/src/backend/#taler-merchant-httpd_private-patch-templates-ID.c# +++ /dev/null @@ -1,197 +0,0 @@ -/* - This file is part of TALER - (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation; either version 3, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public - License along with TALER; see the file COPYING. If not, - see <http://www.gnu.org/licenses/> -*/ - -/** - * @file taler-merchant-httpd_private-patch-templates-ID.c - * @brief implementing PATCH /templates/$ID request handling - * @author Priscilla HUANG - */ -#include "platform.h" -#include "taler-merchant-httpd_private-patch-templates-ID.h" -#include "taler-merchant-httpd_helper.h" -#include <taler/taler_json_lib.h> - - -/** - * How often do we retry the simple INSERT database transaction? - */ -#define MAX_RETRIES 3 - - -/** - * Determine the cause of the PATCH failure in more detail and report. - * - * @param connection connection to report on - * @param instance_id instance we are processing - * @param template_id ID of the product to patch - * @param tp template details we failed to set - */ -static MHD_RESULT -determine_cause (struct MHD_Connection *connection, - const char *instance_id, - const char *template_id, - const struct TALER_MERCHANTDB_TemplateDetails *tp) -{ - struct TALER_MERCHANTDB_TemplateDetails tpx; - enum GNUNET_DB_QueryStatus qs; - - qs = TMH_db->lookup_template (TMH_db->cls, - instance_id, - template_id, - &tpx); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "unexpected serialization problem"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, - template_id); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; /* do below */ - } - - { - enum TALER_ErrorCode ec; - - ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - TALER_MERCHANTDB_template_details_free (&tpx); - GNUNET_break (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE != ec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - ec, - NULL); - } -} - - -/** - * PATCH configuration of an existing instance, given its configuration. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] hc context with further information about the request - * @return MHD result code - */ -MHD_RESULT -TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) -{ - struct TMH_MerchantInstance *mi = hc->instance; - const char *template_id = hc->infix; - struct TALER_MERCHANTDB_TemplateDetails tp = {0}; - enum GNUNET_DB_QueryStatus qs; - uint32_t pos_algorithm; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("template_description", - (const char **) &tp.template_description), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("pos_algorithm", - &pos_algorithm), - 0), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("pos_key", - (const char **) &tp.pos_key), - NULL), - GNUNET_JSON_spec_json ("template_contract", - &tp.template_contract), - GNUNET_JSON_spec_end () - }; - - GNUNET_assert (NULL != mi); - GNUNET_assert (NULL != template_id); - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (connection, - hc->request_body, - spec); - if (GNUNET_OK != res) - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; - } - - tp.pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos_algorithm; - if (! TMH_template_contract_valid (tp.template_contract)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "template_contract"); - } - - qs = TMH_db->update_template (TMH_db->cls, - mi->settings.id, - template_id, - &tp); - { - MHD_RESULT ret = MHD_NO; - - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - NULL); - break; - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "unexpected serialization problem"); - break; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - ret = determine_cause (connection, - mi->settings.id, - template_id, - &tp); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - ret = TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - break; - } - GNUNET_JSON_parse_free (spec); - return ret; - } -} - - -/* end of taler-merchant-httpd_private-patch-templates-ID.c */ diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index fed9c0a2..ef5b67bb 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -44,6 +44,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-delete-products-ID.h \ taler-merchant-httpd_private-delete-orders-ID.c \ taler-merchant-httpd_private-delete-orders-ID.h \ + taler-merchant-httpd_private-delete-otp-devices-ID.c \ + taler-merchant-httpd_private-delete-otp-devices-ID.h \ taler-merchant-httpd_private-delete-reserves-ID.c \ taler-merchant-httpd_private-delete-reserves-ID.h \ taler-merchant-httpd_private-delete-templates-ID.c \ @@ -52,6 +54,10 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-delete-transfers-ID.h \ taler-merchant-httpd_private-delete-webhooks-ID.c \ taler-merchant-httpd_private-delete-webhooks-ID.h \ + taler-merchant-httpd_private-get-accounts.c \ + taler-merchant-httpd_private-get-accounts.h \ + taler-merchant-httpd_private-get-accounts-ID.c \ + taler-merchant-httpd_private-get-accounts-ID.h \ taler-merchant-httpd_private-get-instances.c \ taler-merchant-httpd_private-get-instances.h \ taler-merchant-httpd_private-get-instances-ID.c \ @@ -66,6 +72,10 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-get-orders.h \ taler-merchant-httpd_private-get-orders-ID.c \ taler-merchant-httpd_private-get-orders-ID.h \ + taler-merchant-httpd_private-get-otp-devices.c \ + taler-merchant-httpd_private-get-otp-devices.h \ + taler-merchant-httpd_private-get-otp-devices-ID.c \ + taler-merchant-httpd_private-get-otp-devices-ID.h \ taler-merchant-httpd_private-get-reserves.c \ taler-merchant-httpd_private-get-reserves.h \ taler-merchant-httpd_private-get-reserves-ID.c \ @@ -80,16 +90,20 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-get-webhooks.h \ taler-merchant-httpd_private-get-webhooks-ID.c \ taler-merchant-httpd_private-get-webhooks-ID.h \ - taler-merchant-httpd_private-patch-templates-ID.c \ - taler-merchant-httpd_private-patch-templates-ID.h \ - taler-merchant-httpd_private-patch-webhooks-ID.c \ - taler-merchant-httpd_private-patch-webhooks-ID.h \ + taler-merchant-httpd_private-patch-accounts-ID.c \ + taler-merchant-httpd_private-patch-accounts-ID.h \ taler-merchant-httpd_private-patch-instances-ID.c \ taler-merchant-httpd_private-patch-instances-ID.h \ taler-merchant-httpd_private-patch-orders-ID-forget.c \ taler-merchant-httpd_private-patch-orders-ID-forget.h \ + taler-merchant-httpd_private-patch-otp-devices-ID.c \ + taler-merchant-httpd_private-patch-otp-devices-ID.h \ taler-merchant-httpd_private-patch-products-ID.c \ taler-merchant-httpd_private-patch-products-ID.h \ + taler-merchant-httpd_private-patch-templates-ID.c \ + taler-merchant-httpd_private-patch-templates-ID.h \ + taler-merchant-httpd_private-patch-webhooks-ID.c \ + taler-merchant-httpd_private-patch-webhooks-ID.h \ taler-merchant-httpd_private-post-account.c \ taler-merchant-httpd_private-post-account.h \ taler-merchant-httpd_private-post-instances.c \ @@ -102,6 +116,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-post-orders.h \ taler-merchant-httpd_private-post-products.c \ taler-merchant-httpd_private-post-products.h \ + taler-merchant-httpd_private-post-otp-devices.c \ + taler-merchant-httpd_private-post-otp-devices.h \ taler-merchant-httpd_private-post-products-ID-lock.c \ taler-merchant-httpd_private-post-products-ID-lock.h \ taler-merchant-httpd_private-post-reserves.c \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index d8be1edc..9c543958 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -37,14 +37,13 @@ #include "taler-merchant-httpd_private-delete-instances-ID.h" #include "taler-merchant-httpd_private-delete-products-ID.h" #include "taler-merchant-httpd_private-delete-orders-ID.h" +#include "taler-merchant-httpd_private-delete-otp-devices-ID.h" #include "taler-merchant-httpd_private-delete-reserves-ID.h" #include "taler-merchant-httpd_private-delete-templates-ID.h" #include "taler-merchant-httpd_private-delete-transfers-ID.h" #include "taler-merchant-httpd_private-delete-webhooks-ID.h" -#include "taler-merchant-httpd_private-get-webhooks.h" -#include "taler-merchant-httpd_private-get-webhooks-ID.h" -#include "taler-merchant-httpd_private-get-templates.h" -#include "taler-merchant-httpd_private-get-templates-ID.h" +#include "taler-merchant-httpd_private-get-accounts.h" +#include "taler-merchant-httpd_private-get-accounts-ID.h" #include "taler-merchant-httpd_private-get-instances.h" #include "taler-merchant-httpd_private-get-instances-ID.h" #include "taler-merchant-httpd_private-get-instances-ID-kyc.h" @@ -52,19 +51,28 @@ #include "taler-merchant-httpd_private-get-products-ID.h" #include "taler-merchant-httpd_private-get-orders.h" #include "taler-merchant-httpd_private-get-orders-ID.h" +#include "taler-merchant-httpd_private-get-otp-devices.h" +#include "taler-merchant-httpd_private-get-otp-devices-ID.h" #include "taler-merchant-httpd_private-get-reserves.h" #include "taler-merchant-httpd_private-get-reserves-ID.h" #include "taler-merchant-httpd_private-get-rewards-ID.h" #include "taler-merchant-httpd_private-get-rewards.h" +#include "taler-merchant-httpd_private-get-templates.h" +#include "taler-merchant-httpd_private-get-templates-ID.h" #include "taler-merchant-httpd_private-get-transfers.h" -#include "taler-merchant-httpd_private-patch-webhooks-ID.h" -#include "taler-merchant-httpd_private-patch-templates-ID.h" +#include "taler-merchant-httpd_private-get-webhooks.h" +#include "taler-merchant-httpd_private-get-webhooks-ID.h" +#include "taler-merchant-httpd_private-patch-accounts-ID.h" #include "taler-merchant-httpd_private-patch-instances-ID.h" #include "taler-merchant-httpd_private-patch-orders-ID-forget.h" +#include "taler-merchant-httpd_private-patch-otp-devices-ID.h" #include "taler-merchant-httpd_private-patch-products-ID.h" +#include "taler-merchant-httpd_private-patch-templates-ID.h" +#include "taler-merchant-httpd_private-patch-webhooks-ID.h" #include "taler-merchant-httpd_private-post-account.h" #include "taler-merchant-httpd_private-post-instances.h" #include "taler-merchant-httpd_private-post-instances-ID-auth.h" +#include "taler-merchant-httpd_private-post-otp-devices.h" #include "taler-merchant-httpd_private-post-orders.h" #include "taler-merchant-httpd_private-post-orders-ID-refund.h" #include "taler-merchant-httpd_private-post-products.h" @@ -1041,6 +1049,39 @@ url_handler (void *cls, .allow_deleted_instance = true, .handler = &TMH_private_get_transfers }, + /* POST /otp-devices: */ + { + .url_prefix = "/otp-devices", + .method = MHD_HTTP_METHOD_POST, + .handler = &TMH_private_post_otp_devices + }, + /* GET /otp-devices: */ + { + .url_prefix = "/otp-devices", + .method = MHD_HTTP_METHOD_GET, + .handler = &TMH_private_get_otp_devices + }, + /* GET /otp-devices/$ID/: */ + { + .url_prefix = "/otp-devices/", + .method = MHD_HTTP_METHOD_GET, + .have_id_segment = true, + .handler = &TMH_private_get_otp_devices_ID + }, + /* DELETE /otp-devices/$ID/: */ + { + .url_prefix = "/otp-devices/", + .method = MHD_HTTP_METHOD_DELETE, + .have_id_segment = true, + .handler = &TMH_private_delete_otp_devices_ID + }, + /* PATCH /otp-devices/$ID/: */ + { + .url_prefix = "/otp-devices/", + .method = MHD_HTTP_METHOD_PATCH, + .have_id_segment = true, + .handler = &TMH_private_patch_otp_devices_ID + }, /* POST /templates: */ { .url_prefix = "/templates", @@ -1133,17 +1174,39 @@ url_handler (void *cls, in the code... */ .max_upload = 1024 * 1024 * 8 }, - /* POST /account: */ + /* POST /accounts: */ { - .url_prefix = "/account", + .url_prefix = "/accounts", .method = MHD_HTTP_METHOD_POST, .handler = &TMH_private_post_account, /* allow account details of up to 8 kb, that should be plenty */ .max_upload = 1024 * 8 }, - /* DELETE /account/$PAYTO: */ + /* PATCH /accounts/$H_WIRE: */ { - .url_prefix = "/account", + .url_prefix = "/accounts", + .method = MHD_HTTP_METHOD_PATCH, + .handler = &TMH_private_patch_accounts_ID, + .have_id_segment = true, + /* allow account details of up to 8 kb, that should be plenty */ + .max_upload = 1024 * 8 + }, + /* GET /accounts: */ + { + .url_prefix = "/accounts", + .method = MHD_HTTP_METHOD_GET, + .handler = &TMH_private_get_accounts + }, + /* GET /accounts/$H_WIRE: */ + { + .url_prefix = "/accounts", + .method = MHD_HTTP_METHOD_GET, + .have_id_segment = true, + .handler = &TMH_private_get_accounts_ID + }, + /* DELETE /accounts/$H_WIRE: */ + { + .url_prefix = "/accounts", .method = MHD_HTTP_METHOD_DELETE, .handler = &TMH_private_delete_account_ID, .have_id_segment = true @@ -1779,6 +1842,31 @@ url_handler (void *cls, /** + * Callback invoked with information about a bank account. + * + * @param cls closure with a `struct TMH_MerchantInstance *` + * @param ad details about the account + */ +static void +add_account_cb (void *cls, + const struct TALER_MERCHANTDB_AccountDetails *acc) +{ + struct TMH_MerchantInstance *mi = cls; + struct TMH_WireMethod *wm; + + wm = GNUNET_new (struct TMH_WireMethod); + wm->h_wire = acc->h_wire; + wm->payto_uri = GNUNET_strdup (acc->payto_uri); + wm->wire_salt = acc->salt; + wm->wire_method = TALER_payto_get_method (acc->payto_uri); + wm->active = acc->active; + GNUNET_CONTAINER_DLL_insert (mi->wm_head, + mi->wm_tail, + wm); +} + + +/** * Function called during startup to add all known instances to our * hash map in memory for faster lookups when we receive requests. * @@ -1787,20 +1875,17 @@ url_handler (void *cls, * @param merchant_priv private key of the instance, NULL if not available * @param is detailed configuration settings for the instance * @param ias authentication settings for the instance - * @param accounts_length length of the @a accounts array - * @param accounts list of accounts of the merchant */ static void add_instance_cb (void *cls, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPrivateKeyP *merchant_priv, const struct TALER_MERCHANTDB_InstanceSettings *is, - const struct TALER_MERCHANTDB_InstanceAuthSettings *ias, - unsigned int accounts_length, - const struct TALER_MERCHANTDB_AccountDetails accounts[]) + const struct TALER_MERCHANTDB_InstanceAuthSettings *ias) { struct TMH_MerchantInstance *mi; - + enum GNUNET_DB_QueryStatus qs; + (void) cls; mi = TMH_lookup_instance (is->id); if (NULL != mi) @@ -1829,20 +1914,15 @@ add_instance_cb (void *cls, else mi->deleted = true; mi->merchant_pub = *merchant_pub; - for (unsigned int i = 0; i<accounts_length; i++) + qs = TMH_db->select_accounts (TMH_db->cls, + mi->settings.id, + &add_account_cb, + mi); + if (0 > qs) { - const struct TALER_MERCHANTDB_AccountDetails *acc = &accounts[i]; - struct TMH_WireMethod *wm; - - wm = GNUNET_new (struct TMH_WireMethod); - wm->h_wire = acc->h_wire; - wm->payto_uri = GNUNET_strdup (acc->payto_uri); - wm->wire_salt = acc->salt; - wm->wire_method = TALER_payto_get_method (acc->payto_uri); - wm->active = acc->active; - GNUNET_CONTAINER_DLL_insert (mi->wm_head, - mi->wm_tail, - wm); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error loading accounts of `%s' from database\n", + mi->settings.id); } GNUNET_assert (GNUNET_OK == TMH_add_instance (mi)); diff --git a/src/backend/taler-merchant-httpd_post-using-templates.c b/src/backend/taler-merchant-httpd_post-using-templates.c index 74eb0f44..67fc4b1e 100644 --- a/src/backend/taler-merchant-httpd_post-using-templates.c +++ b/src/backend/taler-merchant-httpd_post-using-templates.c @@ -73,7 +73,6 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, struct TMH_HandlerContext *hc) { struct TMH_MerchantInstance *mi = hc->instance; - MHD_RESULT mret; const char *template_id = hc->infix; const char *summary = NULL; const char *fulfillment_url = NULL; @@ -252,6 +251,9 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, } no_summary = (NULL == summary); fake_body = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_id", + uc->etp.otp_id)), GNUNET_JSON_pack_object_steal ( "order", GNUNET_JSON_PACK ( @@ -276,11 +278,8 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, } uc->ihc.request_body = fake_body; - mret = TMH_private_post_orders_with_pos_secrets ( + return TMH_private_post_orders ( NULL, /* not even used */ connection, - &uc->ihc, - uc->etp.pos_key, - uc->etp.pos_algorithm); - return mret; + &uc->ihc); } diff --git a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c new file mode 100644 index 00000000..b147b84f --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c @@ -0,0 +1,78 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-delete-otp-devices-ID.c + * @brief implement DELETE /otp-devices/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-delete-otp-devices-ID.h" +#include <taler/taler_json_lib.h> + + +/** + * Handle a DELETE "/otp-devices/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_delete_otp_devices_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + enum GNUNET_DB_QueryStatus qs; + + (void) rh; + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != hc->infix); + qs = TMH_db->delete_otp (TMH_db->cls, + mi->settings.id, + hc->infix); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "delete_otp"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "delete_otp (soft)"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, + hc->infix); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + } + GNUNET_assert (0); + return MHD_NO; +} + + +/* end of taler-merchant-httpd_private-delete-otp-devices-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.h b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.h new file mode 100644 index 00000000..cd129d0d --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-delete-otp-devices-ID.h + * @brief implement DELETE /otp-devices/$ID/ + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_OTP_DEVICES_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_OTP_DEVICES_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a DELETE "/otp-devices/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_delete_otp_devices_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-delete-otp-devices-ID.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-accounts-ID.c b/src/backend/taler-merchant-httpd_private-get-accounts-ID.c new file mode 100644 index 00000000..703beeca --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-accounts-ID.c @@ -0,0 +1,103 @@ +/* + This file is part of TALER + (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-accounts-ID.c + * @brief implement GET /accounts/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-accounts-ID.h" +#include <taler/taler_json_lib.h> + + +/** + * Handle a GET "/accounts/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_accounts_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + const char *h_wire_s = hc->infix; + struct TALER_MerchantWireHashP h_wire; + struct TALER_MERCHANTDB_AccountDetails tp = { 0 }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != h_wire_s); + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (h_wire_s, + strlen (h_wire_s), + &h_wire, + sizeof (h_wire))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED, + h_wire_s); + } + qs = TMH_db->select_account (TMH_db->cls, + mi->settings.id, + &h_wire, + &tp); + if (0 > qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_account"); + } + if (0 == qs) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN, + hc->infix); + } + { + MHD_RESULT ret; + + ret = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_string ("payto_uri", + tp.payto_uri), + GNUNET_JSON_pack_data_auto ("h_wire", + &tp.h_wire), + GNUNET_JSON_pack_data_auto ("salt", + &tp.salt), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("credit_facade_url", + tp.credit_facade_url))); + /* We do not return the credentials, as they may + be sensitive */ + json_decref (tp.credit_facade_credentials); + GNUNET_free (tp.payto_uri); + GNUNET_free (tp.credit_facade_url); + return ret; + } +} + + +/* end of taler-merchant-httpd_private-get-accounts-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-accounts-ID.h b/src/backend/taler-merchant-httpd_private-get-accounts-ID.h new file mode 100644 index 00000000..da5cb729 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-accounts-ID.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-accounts-ID.h + * @brief implement GET /accounts/$ID/ + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/accounts/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_accounts_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-get-accounts-ID.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-accounts.c b/src/backend/taler-merchant-httpd_private-get-accounts.c new file mode 100644 index 00000000..92ebb368 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-accounts.c @@ -0,0 +1,78 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-accounts.c + * @brief implement GET /accounts + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-accounts.h" + + +/** + * Add account details to our JSON array. + * + * @param cls a `json_t *` JSON array to build + * @param ad details about the account + */ +static void +add_account (void *cls, + const struct TALER_MERCHANTDB_AccountDetails *ad) +{ + json_t *pa = cls; + + GNUNET_assert (0 == + json_array_append_new ( + pa, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("payto_uri", + ad->payto_uri), + GNUNET_JSON_pack_data_auto ("h_wire", + &ad->h_wire)))); +} + + +MHD_RESULT +TMH_private_get_accounts (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + json_t *pa; + enum GNUNET_DB_QueryStatus qs; + + pa = json_array (); + GNUNET_assert (NULL != pa); + qs = TMH_db->select_accounts (TMH_db->cls, + hc->instance->settings.id, + &add_account, + pa); + if (0 > qs) + { + GNUNET_break (0); + json_decref (pa); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + } + return TALER_MHD_REPLY_JSON_PACK (connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_array_steal ("accounts", + pa)); +} + + +/* end of taler-merchant-httpd_private-get-accounts.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-accounts.h b/src/backend/taler-merchant-httpd_private-get-accounts.h new file mode 100644 index 00000000..0e9897cf --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-accounts.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-accounts.h + * @brief implement GET /accounts + * @author Priscilla HUANG + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/accounts" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_accounts (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-get-accounts.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c new file mode 100644 index 00000000..747e30cd --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c @@ -0,0 +1,83 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-otp-devices-ID.c + * @brief implement GET /otp-devices/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-otp-devices-ID.h" +#include <taler/taler_json_lib.h> + + +/** + * Handle a GET "/otp-devices/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_otp_devices_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_assert (NULL != mi); + qs = TMH_db->select_otp (TMH_db->cls, + mi->settings.id, + hc->infix, + &tp); + if (0 > qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select_otp"); + } + if (0 == qs) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, + hc->infix); + } + { + MHD_RESULT ret; + + /* Note: we deliberately (by design) do not return the otp_key */ + ret = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_string ("device_description", + tp.otp_description), + GNUNET_JSON_pack_uint64 ("otp_algorithm", + tp.otp_algorithm), + GNUNET_JSON_pack_uint64 ("otp_ctr", + tp.otp_ctr)); + GNUNET_free (tp.otp_description); + GNUNET_free (tp.otp_key); + return ret; + } +} + + +/* end of taler-merchant-httpd_private-get-otp-devices-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.h b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.h new file mode 100644 index 00000000..78834f67 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-otp-devices-ID.h + * @brief implement GET /otp-devices/$ID/ + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/otp-devices/$ID" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_otp_devices_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-get-otp-devices-ID.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices.c b/src/backend/taler-merchant-httpd_private-get-otp-devices.c new file mode 100644 index 00000000..df86842b --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-otp-devices.c @@ -0,0 +1,80 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-otp-devices.c + * @brief implement GET /otp-devices + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-get-otp-devices.h" + + +/** + * Add OTP device details to our JSON array. + * + * @param cls a `json_t *` JSON array to build + * @param template_id ID of the template + * @param template_description human-readable description for the template + */ +static void +add_otp (void *cls, + const char *otp_id, + const char *otp_description) +{ + json_t *pa = cls; + + GNUNET_assert (0 == + json_array_append_new ( + pa, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("otp_device_id", + otp_id), + GNUNET_JSON_pack_string ("device_description", + otp_description)))); +} + + +MHD_RESULT +TMH_private_get_otp_devices (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + json_t *pa; + enum GNUNET_DB_QueryStatus qs; + + pa = json_array (); + GNUNET_assert (NULL != pa); + qs = TMH_db->lookup_otp_devices (TMH_db->cls, + hc->instance->settings.id, + &add_otp, + pa); + if (0 > qs) + { + GNUNET_break (0); + json_decref (pa); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + } + return TALER_MHD_REPLY_JSON_PACK (connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_array_steal ("otp_devices", + pa)); +} + + +/* end of taler-merchant-httpd_private-get-otp-devices.c */ diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices.h b/src/backend/taler-merchant-httpd_private-get-otp-devices.h new file mode 100644 index 00000000..a97ca179 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-get-otp-devices.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-merchant-httpd_private-get-otp-devices.h + * @brief implement GET /otp-devices + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_H +#define TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_H + +#include "taler-merchant-httpd.h" + + +/** + * Handle a GET "/otp-devices" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_get_otp_devices (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/* end of taler-merchant-httpd_private-get-otp-devices.h */ +#endif diff --git a/src/backend/taler-merchant-httpd_private-get-templates-ID.c b/src/backend/taler-merchant-httpd_private-get-templates-ID.c index bdb1de9d..e9dfc00f 100644 --- a/src/backend/taler-merchant-httpd_private-get-templates-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-templates-ID.c @@ -68,16 +68,13 @@ TMH_private_get_templates_ID (const struct TMH_RequestHandler *rh, MHD_HTTP_OK, GNUNET_JSON_pack_string ("template_description", tp.template_description), - GNUNET_JSON_pack_uint64 ("pos_algorithm", - tp.pos_algorithm), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("pos_key", - tp.pos_key)), + GNUNET_JSON_pack_string ("otp_id", + tp.otp_id)), GNUNET_JSON_pack_object_steal ("template_contract", tp.template_contract)); GNUNET_free (tp.template_description); - GNUNET_free (tp.pos_key); - + GNUNET_free (tp.otp_id); return ret; } } diff --git a/src/backend/taler-merchant-httpd_private-patch-accounts-ID.c b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.c new file mode 100644 index 00000000..04fe4ce5 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.c @@ -0,0 +1,132 @@ +/* + This file is part of TALER + (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-patch-accounts-ID.c + * @brief implementing PATCH /accounts/$ID request handling + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-patch-accounts-ID.h" +#include "taler-merchant-httpd_helper.h" +#include <taler/taler_json_lib.h> + + +/** + * PATCH configuration of an existing instance, given its configuration. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_patch_accounts_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + const char *h_wire_s = hc->infix; + enum GNUNET_DB_QueryStatus qs; + const json_t *cfc; + const char *cfu; + struct TALER_MerchantWireHashP h_wire; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("credit_facade_url", + (const char **) &cfu), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("credit_facade_credentials", + &cfc), + NULL), + GNUNET_JSON_spec_end () + }; + + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != h_wire_s); + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (h_wire_s, + strlen (h_wire_s), + &h_wire, + sizeof (h_wire))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED, + h_wire_s); + } + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + + qs = TMH_db->update_account (TMH_db->cls, + mi->settings.id, + &h_wire, + cfu, + cfc); + { + MHD_RESULT ret = MHD_NO; + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "update_account"); + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "unexpected serialization problem"); + break; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN, + h_wire_s); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + ret = TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + break; + } + GNUNET_JSON_parse_free (spec); + return ret; + } +} + + +/* end of taler-merchant-httpd_private-patch-accounts-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-patch-accounts-ID.h b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.h new file mode 100644 index 00000000..752fb958 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.h @@ -0,0 +1,43 @@ +/* + This file is part of TALER + (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-patch-accounts-ID.h + * @brief implementing PATCH /accounts request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_ACCOUNTS_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_ACCOUNTS_ID_H +#include "taler-merchant-httpd.h" + + +/** + * PATCH configuration of an existing instance, given its configuration. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_patch_accounts_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c index bc6e3aae..027d5869 100644 --- a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c +++ b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c @@ -63,14 +63,11 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, struct TMH_HandlerContext *hc) { struct TALER_MERCHANTDB_InstanceSettings is; - const json_t *accounts; const char *name; const char *uts = "business"; struct TMH_WireMethod *wm_head = NULL; struct TMH_WireMethod *wm_tail = NULL; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("accounts", - &accounts), GNUNET_JSON_spec_string ("name", &name), GNUNET_JSON_spec_mark_optional ( @@ -102,7 +99,6 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, GNUNET_JSON_spec_end () }; enum GNUNET_DB_QueryStatus qs; - bool committed = false; GNUNET_assert (NULL != mi); memset (&is, @@ -162,14 +158,6 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, "jurisdiction"); } - if (! TMH_accounts_array_valid (accounts)) - { - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PAYTO_URI_MALFORMED, - NULL); - } for (unsigned int retry = 0; retry<MAX_RETRIES; retry++) { /* Cleanup after earlier loops */ @@ -236,258 +224,13 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, goto giveup; } } - - /* Check for changes in accounts */ - { - unsigned int len = json_array_size (accounts); - struct TMH_WireMethod *matches[GNUNET_NZL (len)]; - bool updated[GNUNET_NZL (len)]; - - memset (matches, - 0, - sizeof (matches)); - memset (updated, - 0, - sizeof (updated)); - for (struct TMH_WireMethod *wm = mi->wm_head; - NULL != wm; - wm = wm->next) - { - bool matched = false; - for (unsigned int i = 0; i<len; i++) - { - json_t *account = json_array_get (accounts, - i); - enum GNUNET_GenericReturnValue ret; - - ret = TMH_cmp_wire_account (account, - wm); - switch (ret) - { - case GNUNET_SYSERR: - continue; - case GNUNET_NO: - matched = true; - /* our own existing payto URIs should be unique, that is no - duplicates in the list, so we cannot match twice */ - GNUNET_assert (NULL == matches[i]); - matches[i] = wm; - updated[i] = true; - break; - case GNUNET_YES: - matched = true; - /* our own existing payto URIs should be unique, that is no - duplicates in the list, so we cannot match twice */ - GNUNET_assert (NULL == matches[i]); - matches[i] = wm; - break; - } - } - - /* delete unmatched (= removed) accounts */ - if ( (! matched) && - (wm->active) ) - { - /* Account was REMOVED */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Existing account `%s' not found, deactivating it.\n", - wm->payto_uri); - wm->deleting = true; - qs = TMH_db->inactivate_account (TMH_db->cls, - mi->settings.id, - &wm->h_wire); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - TMH_db->rollback (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - else - goto giveup; - } - } - } /* for (wm) */ - - /* handle updates */ - for (unsigned int i = 0; i<len; i++) - { - struct TMH_WireMethod *wm = matches[i]; - - if (! updated[i]) - continue; - GNUNET_assert (NULL != wm); - { - struct TALER_MERCHANTDB_AccountDetails ad = { - .payto_uri = wm->payto_uri, - .h_wire = wm->h_wire, - .salt = wm->wire_salt, - .credit_facade_url = wm->credit_facade_url, - .credit_facade_credentials = wm->credit_facade_credentials, - .active = true - }; - - qs = TMH_db->update_account (TMH_db->cls, - mi->settings.id, - &ad); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - TMH_db->rollback (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - else - goto giveup; - } - } /* ad scope */ - } /* for possible updates */ - - /* Find _new_ accounts or accounts to only enable */ - for (unsigned int i = 0; i<len; i++) - { - json_t *account = json_array_get (accounts, - i); - struct TMH_WireMethod *wm = matches[i]; - - if (NULL != wm) - { - if (updated[i]) - continue; /* handled above */ - if (! wm->active) - { - qs = TMH_db->activate_account (TMH_db->cls, - mi->settings.id, - &wm->h_wire); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - TMH_db->rollback (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - else - goto giveup; - } - } - wm->enabling = true; - continue; - } - - { - const char *credit_facade_url = NULL; - const json_t *credit_facade_credentials = NULL; - const char *uri; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("payto_uri", - &uri), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("credit_facade_url", - &credit_facade_url), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("credit_facade_credentials", - &credit_facade_credentials), - NULL), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (connection, - account, - ispec); - if (GNUNET_OK != res) - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Adding NEW account `%s'\n", - uri); - wm = TMH_setup_wire_account (uri, - credit_facade_url, - credit_facade_credentials); - GNUNET_assert (NULL != wm); /* checked payto_uri validity earlier */ - GNUNET_CONTAINER_DLL_insert (wm_head, - wm_tail, - wm); - } /* ispec scope */ - - { - struct TALER_MERCHANTDB_AccountDetails ad = { - .payto_uri = wm->payto_uri, - .h_wire = wm->h_wire, - .salt = wm->wire_salt, - .credit_facade_url = wm->credit_facade_url, - .credit_facade_credentials = wm->credit_facade_credentials, - .active = true - }; - - qs = TMH_db->insert_account (TMH_db->cls, - mi->settings.id, - &ad); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - TMH_db->rollback (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - else - goto giveup; - } - } /* ad variable scope */ - } /* for (i) to find new accounts */ - } /* scope for checking for account changes */ - - { - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) - }; - - TMH_db->event_notify (TMH_db->cls, - &es, - NULL, - 0); - } qs = TMH_db->commit (TMH_db->cls); retry: if (GNUNET_DB_STATUS_SOFT_ERROR == qs) continue; - if (qs >= 0) - committed = true; break; } /* for(... MAX_RETRIES) */ giveup: - /* Deactivate existing wire methods that were removed above */ - for (struct TMH_WireMethod *wm = mi->wm_head; - NULL != wm; - wm = wm->next) - { - /* We did not flip the 'active' bits earlier because the - DB transaction could still fail. Now it is time to update our - runtime state. */ - GNUNET_assert (! (wm->deleting & wm->enabling)); - if (committed) - { - if (wm->deleting) - wm->active = false; - if (wm->enabling) - wm->active = true; - } - wm->deleting = false; - wm->enabling = false; - } - if (! committed) - { - struct TMH_WireMethod *wm; - - while (NULL != (wm = wm_head)) - { - GNUNET_CONTAINER_DLL_remove (wm_head, - wm_tail, - wm); - free_wm (wm); - } - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_COMMIT_FAILED, - NULL); - } - /* Update our 'settings' */ GNUNET_free (mi->settings.name); GNUNET_free (mi->settings.email); @@ -508,23 +251,6 @@ giveup: if (NULL != is.logo) mi->settings.logo = GNUNET_strdup (is.logo); - /* Add 'new' wire methods to our list */ - { - struct TMH_WireMethod *wm; - - /* Note: this _could_ be done more efficiently if - someone wrote a GNUNET_CONTAINER_DLL_merge()... */ - while (NULL != (wm = wm_head)) - { - GNUNET_CONTAINER_DLL_remove (wm_head, - wm_tail, - wm); - GNUNET_CONTAINER_DLL_insert (mi->wm_head, - mi->wm_tail, - wm); - } - } - GNUNET_JSON_parse_free (spec); TMH_reload_instances (mi->settings.id); return TALER_MHD_reply_static (connection, diff --git a/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.c b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.c new file mode 100644 index 00000000..f0fc8b0b --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.c @@ -0,0 +1,122 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-patch-otp-devices-ID.c + * @brief implementing PATCH /otp-devices/$ID request handling + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-patch-otp-devices-ID.h" +#include "taler-merchant-httpd_helper.h" +#include <taler/taler_json_lib.h> + + +/** + * PATCH OTP device of an existing instance. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_patch_otp_devices_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + const char *device_id = hc->infix; + struct TALER_MERCHANTDB_OtpDeviceDetails tp = {0}; + enum GNUNET_DB_QueryStatus qs; + uint32_t otp_algorithm; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_description", + (const char **) &tp.otp_description), + GNUNET_JSON_spec_uint32 ("otp_algorithm", + &otp_algorithm), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_ctr", + &tp.otp_ctr), + NULL), + GNUNET_JSON_spec_string ("otp_key", + (const char **) &tp.otp_key), + GNUNET_JSON_spec_end () + }; + + GNUNET_assert (NULL != mi); + GNUNET_assert (NULL != device_id); + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + + tp.otp_algorithm = (enum TALER_MerchantConfirmationAlgorithm) otp_algorithm; + + qs = TMH_db->update_otp (TMH_db->cls, + mi->settings.id, + device_id, + &tp); + { + MHD_RESULT ret = MHD_NO; + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "update_pos"); + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "unexpected serialization problem"); + break; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, + device_id); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + ret = TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + break; + } + GNUNET_JSON_parse_free (spec); + return ret; + } +} + + +/* end of taler-merchant-httpd_private-patch-otp-devices-ID.c */ diff --git a/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.h b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.h new file mode 100644 index 00000000..eef1dd0a --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-patch-otp-devices-ID.h + * @brief implementing PATCH /otp-devices/$ID request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_OTP_DEVICES_ID_H +#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_OTP_DEVICES_ID_H + +#include "taler-merchant-httpd.h" + + +/** + * PATCH configuration of an existing instance, given its configuration. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_patch_otp_devices_ID (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-patch-templates-ID.c b/src/backend/taler-merchant-httpd_private-patch-templates-ID.c index b5938368..68e0a478 100644 --- a/src/backend/taler-merchant-httpd_private-patch-templates-ID.c +++ b/src/backend/taler-merchant-httpd_private-patch-templates-ID.c @@ -29,12 +29,6 @@ /** - * How often do we retry the simple INSERT database transaction? - */ -#define MAX_RETRIES 3 - - -/** * Determine the cause of the PATCH failure in more detail and report. * * @param connection connection to report on @@ -109,17 +103,12 @@ TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh, const char *template_id = hc->infix; struct TALER_MERCHANTDB_TemplateDetails tp = {0}; enum GNUNET_DB_QueryStatus qs; - uint32_t pos_algorithm; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("template_description", (const char **) &tp.template_description), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("pos_algorithm", - &pos_algorithm), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("pos_key", - (const char **) &tp.pos_key), + GNUNET_JSON_spec_string ("otp_id", + (const char **) &tp.otp_id), NULL), GNUNET_JSON_spec_json ("template_contract", &tp.template_contract), @@ -140,7 +129,6 @@ TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh, : MHD_NO; } - tp.pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos_algorithm; if (! TMH_template_contract_valid (tp.template_contract)) { GNUNET_break_op (0); diff --git a/src/backend/taler-merchant-httpd_private-post-account.c b/src/backend/taler-merchant-httpd_private-post-account.c index f9be253c..73abfe48 100644 --- a/src/backend/taler-merchant-httpd_private-post-account.c +++ b/src/backend/taler-merchant-httpd_private-post-account.c @@ -19,7 +19,7 @@ /** * @file taler-merchant-httpd_private-post-account.c - * @brief implementing POST /private/account request handling + * @brief implementing POST /private/accounts request handling * @author Christian Grothoff */ #include "platform.h" @@ -173,11 +173,12 @@ TMH_private_post_account (const struct TMH_RequestHandler *rh, /* Note: we may not need to do this, as we notified about the account change above. But also hardly hurts. */ TMH_reload_instances (mi->settings.id); - return TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); + return TALER_MHD_REPLY_JSON_PACK (connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_data_auto ("salt", + &wm->wire_salt), + GNUNET_JSON_pack_data_auto ("h_wire", + &wm->h_wire)); } diff --git a/src/backend/taler-merchant-httpd_private-post-instances.c b/src/backend/taler-merchant-httpd_private-post-instances.c index 6c9727e9..398a846d 100644 --- a/src/backend/taler-merchant-httpd_private-post-instances.c +++ b/src/backend/taler-merchant-httpd_private-post-instances.c @@ -37,56 +37,6 @@ /** - * Check if the array of @a payto_uris contains exactly the same - * URIs as those already in @a mi (possibly in a different order). - * - * @param mi a merchant instance with accounts - * @param accounts a JSON array with accounts (presumably) - * @return true if they are 'equal', false if not or of payto_uris is not an array - */ -static bool -accounts_equal (const struct TMH_MerchantInstance *mi, - const json_t *accounts) -{ - if (! json_is_array (accounts)) - return false; - { - unsigned int len = json_array_size (accounts); - enum GNUNET_GenericReturnValue matches[GNUNET_NZL (len)]; - - for (unsigned int i = 0; i<len; i++) - matches[i] = GNUNET_SYSERR; - for (struct TMH_WireMethod *wm = mi->wm_head; - NULL != wm; - wm = wm->next) - { - for (unsigned int i = 0; i<len; i++) - { - json_t *account = json_array_get (accounts, - i); - enum GNUNET_GenericReturnValue ret; - - ret = TMH_cmp_wire_account (account, - wm); - if (GNUNET_SYSERR == ret) - continue; - if (GNUNET_SYSERR != matches[i]) - { - GNUNET_break (0); - return false; /* duplicate entry!? */ - } - matches[i] = ret; - } - } - for (unsigned int i = 0; i<len; i++) - if (GNUNET_YES != matches[i]) - return false; - } - return true; -} - - -/** * Generate an instance, given its configuration. * * @param rh context of the handler @@ -101,15 +51,12 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, { struct TALER_MERCHANTDB_InstanceSettings is; struct TALER_MERCHANTDB_InstanceAuthSettings ias; - const json_t *accounts; const char *auth_token = NULL; const char *uts = "business"; struct TMH_WireMethod *wm_head = NULL; struct TMH_WireMethod *wm_tail = NULL; const json_t *jauth; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("accounts", - &accounts), GNUNET_JSON_spec_string ("id", (const char **) &is.id), GNUNET_JSON_spec_string ("name", @@ -179,16 +126,6 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; } - /* check accounts for well-formedness */ - if (! TMH_accounts_array_valid (accounts)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PAYTO_URI_MALFORMED, - NULL); - } - /* check 'id' well-formed */ { static bool once; @@ -292,9 +229,7 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, is.default_wire_transfer_delay)) && (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay, ==, - is.default_pay_delay)) && - (accounts_equal (mi, - accounts)) ) + is.default_pay_delay)) ) { return TALER_MHD_reply_static (connection, MHD_HTTP_NO_CONTENT, @@ -312,50 +247,6 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, } } - /* convert provided payto URIs into internal data structure with salts */ - { - unsigned int len = json_array_size (accounts); - - for (unsigned int i = 0; i<len; i++) - { - json_t *account = json_array_get (accounts, - i); - const char *credit_facade_url = NULL; - const json_t *credit_facade_credentials = NULL; - const char *uri; - struct TMH_WireMethod *wm; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("payto_uri", - &uri), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("credit_facade_url", - &credit_facade_url), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("credit_facade_credentials", - &credit_facade_credentials), - NULL), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (connection, - account, - ispec); - if (GNUNET_OK != res) - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; - wm = TMH_setup_wire_account (uri, - credit_facade_url, - credit_facade_credentials); - GNUNET_assert (NULL != wm); - GNUNET_CONTAINER_DLL_insert (wm_head, - wm_tail, - wm); - } - } - /* handle authentication token setup */ if (NULL == auth_token) { @@ -431,44 +322,6 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, TMH_instance_decref (mi); return ret; } - for (struct TMH_WireMethod *wm = wm_head; - NULL != wm; - wm = wm->next) - { - struct TALER_MERCHANTDB_AccountDetails ad = { - .payto_uri = wm->payto_uri, - .salt = wm->wire_salt, - .h_wire = wm->h_wire, - .credit_facade_url = wm->credit_facade_url, - .credit_facade_credentials = wm->credit_facade_credentials, - .active = wm->active - }; - - qs = TMH_db->insert_account (TMH_db->cls, - mi->settings.id, - &ad); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - break; - } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - TMH_db->rollback (TMH_db->cls); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - break; - goto retry; - } - { - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) - }; - - TMH_db->event_notify (TMH_db->cls, - &es, - NULL, - 0); - } qs = TMH_db->commit (TMH_db->cls); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 1c888508..34fac6a0 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -1760,11 +1760,17 @@ merge_inventory (struct OrderContext *oc) } +/** + * Parse the basics of the client request. + * + * @param[in,out] oc order context to process + */ static void parse_order_request (struct OrderContext *oc) { const json_t *ip = NULL; const json_t *uuid = NULL; + const char *otp_id = NULL; bool create_token = true; /* default */ struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_json ("order", @@ -1789,6 +1795,10 @@ parse_order_request (struct OrderContext *oc) GNUNET_JSON_spec_bool ("create_token", &create_token), NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("otp_id", + &otp_id), + NULL), GNUNET_JSON_spec_end () }; enum GNUNET_GenericReturnValue ret; @@ -1808,6 +1818,43 @@ parse_order_request (struct OrderContext *oc) GNUNET_TIME_relative2s (oc->refund_delay, false)); TMH_db->expire_locks (TMH_db->cls); + if (NULL != otp_id) + { + struct TALER_MERCHANTDB_OtpDeviceDetails td; + enum GNUNET_DB_QueryStatus qs; + + qs = TMH_db->select_otp (TMH_db->cls, + oc->hc->instance->settings.id, + otp_id, + &td); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + reply_with_error (oc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select_otp"); + return; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + reply_with_error (oc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_SOFT_FAILURE, + "select_otp"); + return; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + reply_with_error (oc, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, + otp_id); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + oc->pos_key = td.otp_key; + oc->pos_algorithm = td.otp_algorithm; + } if (create_token) { GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, @@ -1912,12 +1959,10 @@ parse_order_request (struct OrderContext *oc) MHD_RESULT -TMH_private_post_orders_with_pos_secrets ( +TMH_private_post_orders ( const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, - struct TMH_HandlerContext *hc, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm) + struct TMH_HandlerContext *hc) { struct OrderContext *oc = hc->ctx; @@ -1928,8 +1973,6 @@ TMH_private_post_orders_with_pos_secrets ( hc->cc = &clean_order; oc->connection = connection; oc->hc = hc; - oc->pos_key = pos_key; - oc->pos_algorithm = pos_algorithm; } while (1) { @@ -1976,18 +2019,4 @@ TMH_private_post_orders_with_pos_secrets ( } -MHD_RESULT -TMH_private_post_orders ( - const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) -{ - return TMH_private_post_orders_with_pos_secrets (rh, - connection, - hc, - NULL, - TALER_MCA_NONE); -} - - /* end of taler-merchant-httpd_private-post-orders.c */ diff --git a/src/backend/taler-merchant-httpd_private-post-orders.h b/src/backend/taler-merchant-httpd_private-post-orders.h index cbbb59c0..f1127bec 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.h +++ b/src/backend/taler-merchant-httpd_private-post-orders.h @@ -46,25 +46,5 @@ TMH_private_post_orders (const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, struct TMH_HandlerContext *hc); -/** - * Generate an order. We add the fields 'exchanges', 'merchant_pub', and - * 'H_wire' to the order gotten from the frontend, as well as possibly other - * fields if the frontend did not provide them. Returns the order_id. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] hc context with further information about the request - * @param pos_key key identifying the POS, can be NULL - * @param pos_algorithm algorithm for computing the POS confirmation - * @return MHD result code - */ -MHD_RESULT -TMH_private_post_orders_with_pos_secrets ( - const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm); - #endif diff --git a/src/backend/taler-merchant-httpd_private-post-otp-devices.c b/src/backend/taler-merchant-httpd_private-post-otp-devices.c new file mode 100644 index 00000000..5521ce97 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-otp-devices.c @@ -0,0 +1,201 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-post-otp-devices.c + * @brief implementing POST /otp-devices request handling + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-merchant-httpd_private-post-otp-devices.h" +#include "taler-merchant-httpd_helper.h" +#include <taler/taler_json_lib.h> + + +/** + * How often do we retry the simple INSERT database transaction? + */ +#define MAX_RETRIES 3 + + +/** + * Check if the two otp-devices are identical. + * + * @param t1 device to compare + * @param t2 other device to compare + * @return true if they are 'equal', false if not or of payto_uris is not an array + */ +static bool +otp_devices_equal (const struct TALER_MERCHANTDB_OtpDeviceDetails *t1, + const struct TALER_MERCHANTDB_OtpDeviceDetails *t2) +{ + return ( (0 == strcmp (t1->otp_description, + t2->otp_description)) && + (0 == strcmp (t1->otp_key, + t2->otp_key) ) && + (t1->otp_ctr == t2->otp_ctr) && + (t1->otp_algorithm == t2->otp_algorithm) ); +} + + +MHD_RESULT +TMH_private_post_otp_devices (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TMH_MerchantInstance *mi = hc->instance; + struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 }; + const char *device_id; + enum GNUNET_DB_QueryStatus qs; + uint32_t otp_algorithm; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_device_id", + &device_id), + GNUNET_JSON_spec_string ("otp_device_description", + (const char **) &tp.otp_description), + GNUNET_JSON_spec_uint32 ("otp_algorithm", + &otp_algorithm), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_ctr", + &tp.otp_ctr), + NULL), + GNUNET_JSON_spec_string ("otp_key", + (const char **) &tp.otp_key), + GNUNET_JSON_spec_end () + }; + + GNUNET_assert (NULL != mi); + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + } + tp.otp_algorithm = (enum TALER_MerchantConfirmationAlgorithm) otp_algorithm; + + /* finally, interact with DB until no serialization error */ + for (unsigned int i = 0; i<MAX_RETRIES; i++) + { + /* Test if a OTP device of this id is known */ + struct TALER_MERCHANTDB_OtpDeviceDetails etp; + + if (GNUNET_OK != + TMH_db->start (TMH_db->cls, + "/post otp-devices")) + { + GNUNET_break (0); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_START_FAILED, + NULL); + } + qs = TMH_db->select_otp (TMH_db->cls, + mi->settings.id, + device_id, + &etp); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + /* Clean up and fail hard */ + GNUNET_break (0); + TMH_db->rollback (TMH_db->cls); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SOFT_ERROR: + /* restart transaction */ + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* Good, we can proceed! */ + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* idempotency check: is etp == tp? */ + { + bool eq; + + eq = otp_devices_equal (&tp, + &etp); + GNUNET_free (etp.otp_description); + GNUNET_free (etp.otp_key); + TMH_db->rollback (TMH_db->cls); + GNUNET_JSON_parse_free (spec); + return eq + ? TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0) + : TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_OTP_DEVICES_CONFLICT_OTP_DEVICE_EXISTS, + device_id); + } + } /* end switch (qs) */ + + qs = TMH_db->insert_otp (TMH_db->cls, + mi->settings.id, + device_id, + &tp); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + TMH_db->rollback (TMH_db->cls); + break; + } + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + { + qs = TMH_db->commit (TMH_db->cls); + if (GNUNET_DB_STATUS_SOFT_ERROR != qs) + break; + } +retry: + GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs); + TMH_db->rollback (TMH_db->cls); + } /* for RETRIES loop */ + GNUNET_JSON_parse_free (spec); + if (qs < 0) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + (GNUNET_DB_STATUS_SOFT_ERROR == qs) + ? TALER_EC_GENERIC_DB_SOFT_FAILURE + : TALER_EC_GENERIC_DB_COMMIT_FAILED, + NULL); + } + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); +} + + +/* end of taler-merchant-httpd_private-post-otp-devices.c */ diff --git a/src/backend/taler-merchant-httpd_private-post-otp-devices.h b/src/backend/taler-merchant-httpd_private-post-otp-devices.h new file mode 100644 index 00000000..96564d08 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-otp-devices.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ + +/** + * @file taler-merchant-httpd_private-post-otp-devices.h + * @brief implementing POST /otp-devices request handling + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_OTP_DEVICES_H +#define TALER_MERCHANT_HTTPD_PRIVATE_POST_OTP_DEVICES_H + +#include "taler-merchant-httpd.h" + + +/** + * Generate an OTP device. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_post_otp_devices (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-post-templates.c b/src/backend/taler-merchant-httpd_private-post-templates.c index 276e225f..4a5d8133 100644 --- a/src/backend/taler-merchant-httpd_private-post-templates.c +++ b/src/backend/taler-merchant-httpd_private-post-templates.c @@ -29,12 +29,6 @@ /** - * How often do we retry the simple INSERT database transaction? - */ -#define MAX_RETRIES 3 - - -/** * Check if the two templates are identical. * * @param t1 template to compare @@ -47,12 +41,12 @@ templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1, { return ( (0 == strcmp (t1->template_description, t2->template_description)) && - ( ( (NULL == t1->pos_key) && - (NULL == t2->pos_key) ) || - ( (NULL != t1->pos_key) && - (NULL != t2->pos_key) && - (0 == strcmp (t1->pos_key, - t2->pos_key))) ) && + ( ( (NULL == t1->otp_id) && + (NULL == t2->otp_id) ) || + ( (NULL != t1->otp_id) && + (NULL != t2->otp_id) && + (0 == strcmp (t1->otp_id, + t2->otp_id))) ) && (1 == json_equal (t1->template_contract, t2->template_contract)) ); } @@ -67,24 +61,20 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh, struct TALER_MERCHANTDB_TemplateDetails tp = { 0 }; const char *template_id; enum GNUNET_DB_QueryStatus qs; - uint32_t pos_algorithm = 0; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("template_id", &template_id), GNUNET_JSON_spec_string ("template_description", (const char **) &tp.template_description), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("pos_algorithm", - &pos_algorithm), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("pos_key", - (const char **) &tp.pos_key), + GNUNET_JSON_spec_string ("otp_id", + (const char **) &tp.otp_id), NULL), GNUNET_JSON_spec_json ("template_contract", &tp.template_contract), GNUNET_JSON_spec_end () }; + uint64_t otp_serial = 0; GNUNET_assert (NULL != mi); { @@ -101,7 +91,6 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh, : MHD_NO; } } - tp.pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos_algorithm; if (! TMH_template_contract_valid (tp.template_contract)) { GNUNET_break_op (0); @@ -115,23 +104,63 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh, "template_contract"); } - /* finally, interact with DB until no serialization error */ - for (unsigned int i = 0; i<MAX_RETRIES; i++) + if (NULL != tp.otp_id) { - /* Test if a template of this id is known */ - struct TALER_MERCHANTDB_TemplateDetails etp; - - if (GNUNET_OK != - TMH_db->start (TMH_db->cls, - "/post templates")) + qs = TMH_db->select_otp_serial (TMH_db->cls, + mi->settings.id, + tp.otp_id, + &otp_serial); + switch (qs) { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_START_FAILED, + TALER_EC_GENERIC_DB_STORE_FAILED, + "select_otp_serial"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, NULL); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; } + } + + qs = TMH_db->insert_template (TMH_db->cls, + mi->settings.id, + template_id, + otp_serial, + &tp); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + break; + } + + { + /* Test if a template of this id is known */ + struct TALER_MERCHANTDB_TemplateDetails etp; + qs = TMH_db->lookup_template (TMH_db->cls, mi->settings.id, template_id, @@ -139,79 +168,45 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh, switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: /* Clean up and fail hard */ GNUNET_break (0); - TMH_db->rollback (TMH_db->cls); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, NULL); - case GNUNET_DB_STATUS_SOFT_ERROR: - /* restart transaction */ - goto retry; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* Good, we can proceed! */ - break; + GNUNET_break (0); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "logic error"); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* idempotency check: is etp == tp? */ - { - bool eq; - - eq = templates_equal (&tp, - &etp); - TALER_MERCHANTDB_template_details_free (&etp); - TMH_db->rollback (TMH_db->cls); - GNUNET_JSON_parse_free (spec); - return eq - ? TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0) - : TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS, - template_id); - } - } /* end switch (qs) */ - - qs = TMH_db->insert_template (TMH_db->cls, - mi->settings.id, - template_id, - &tp); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - TMH_db->rollback (TMH_db->cls); break; } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + /* idempotency check: is etp == tp? */ { - qs = TMH_db->commit (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; + bool eq; + + eq = templates_equal (&tp, + &etp); + TALER_MERCHANTDB_template_details_free (&etp); + TMH_db->rollback (TMH_db->cls); + GNUNET_JSON_parse_free (spec); + return eq + ? TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0) + : TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS, + template_id); } -retry: - GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs); - TMH_db->rollback (TMH_db->cls); - } /* for RETRIES loop */ - GNUNET_JSON_parse_free (spec); - if (qs < 0) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - (GNUNET_DB_STATUS_SOFT_ERROR == qs) - ? TALER_EC_GENERIC_DB_SOFT_FAILURE - : TALER_EC_GENERIC_DB_COMMIT_FAILED, - NULL); } - return TALER_MHD_reply_static (connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); } diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am index 3d7d7827..6081f722 100644 --- a/src/backenddb/Makefile.am +++ b/src/backenddb/Makefile.am @@ -84,6 +84,19 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \ pg_update_instance_auth.h pg_update_instance_auth.c \ pg_inactivate_account.h pg_inactivate_account.c \ pg_activate_account.h pg_activate_account.c \ + pg_insert_otp.h pg_insert_otp.c \ + pg_delete_otp.h pg_delete_otp.c \ + pg_update_otp.h pg_update_otp.c \ + pg_select_otp.h pg_select_otp.c \ + pg_select_otp_serial.h pg_select_otp_serial.c \ + pg_lookup_otp_devices.h pg_lookup_otp_devices.c \ + pg_select_account.h pg_select_account.c \ + pg_select_accounts.h pg_select_accounts.c \ + pg_delete_template.h pg_delete_template.c \ + pg_insert_template.h pg_insert_template.c \ + pg_update_template.h pg_update_template.c \ + pg_lookup_templates.h pg_lookup_templates.c \ + pg_lookup_template.h pg_lookup_template.c \ pg_lookup_products.h pg_lookup_products.c \ pg_lookup_product.h pg_lookup_product.c \ pg_delete_product.h pg_delete_product.c \ diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql index bbc97828..4d286db6 100644 --- a/src/backenddb/merchant-0001.sql +++ b/src/backenddb/merchant-0001.sql @@ -291,6 +291,7 @@ CREATE TABLE IF NOT EXISTS merchant_contract_terms REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE ,order_id TEXT NOT NULL ,contract_terms BYTEA NOT NULL + ,wallet_data BYTEA DEFAULT NULL ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64) ,creation_time INT8 NOT NULL ,pay_deadline INT8 NOT NULL @@ -313,6 +314,8 @@ COMMENT ON COLUMN merchant_contract_terms.merchant_serial IS 'Identifies the instance offering the contract'; COMMENT ON COLUMN merchant_contract_terms.contract_terms IS 'These contract terms include the wallet nonce'; +COMMENT ON COLUMN merchant_contract_terms.wallet_data + IS 'Data provided by the wallet when paying for the contract (subcontract selection, blinded tokens, etc.)'; COMMENT ON COLUMN merchant_contract_terms.h_contract_terms IS 'Hash over contract_terms'; COMMENT ON COLUMN merchant_contract_terms.refund_deadline @@ -643,14 +646,37 @@ COMMENT ON COLUMN merchant_kyc.exchange_url IS 'Which exchange base URL is this KYC status valid for'; +CREATE TABLE IF NOT EXISTS merchant_otp_devices + (otp_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY + ,merchant_serial BIGINT NOT NULL + REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE + ,otp_id TEXT NOT NULL + ,otp_description TEXT NOT NULL + ,otp_key TEXT DEFAULT NULL + ,otp_algorithm INT NOT NULL DEFAULT (0) + ,otp_ctr INT8 NOT NULL DEFAULT (0) + ,UNIQUE (merchant_serial, otp_id) + ); +COMMENT ON TABLE merchant_otp_devices + IS 'OTP device owned by a merchant'; +COMMENT ON COLUMN merchant_otp_devices.otp_description + IS 'Human-readable OTP device description'; +COMMENT ON COLUMN merchant_otp_devices.otp_key + IS 'A base64-encoded key of the point-of-sale. It will be use by the OTP device'; +COMMENT ON COLUMN merchant_otp_devices.otp_algorithm + IS 'algorithm to used to generate the confirmation code. It is linked with the otp_key and otp_ctr'; +COMMENT ON COLUMN merchant_otp_devices.otp_ctr + IS 'counter for counter-based OTP generators'; + + CREATE TABLE IF NOT EXISTS merchant_template (template_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,merchant_serial BIGINT NOT NULL REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE ,template_id TEXT NOT NULL ,template_description TEXT NOT NULL - ,pos_key TEXT DEFAULT NULL - ,pos_algorithm INT NOT NULL DEFAULT (0) + ,otp_device_id BIGINT + REFERENCES merchant_otp_devices (otp_serial) ON DELETE SET NULL ,template_contract TEXT NOT NULL -- in JSON format ,UNIQUE (merchant_serial, template_id) ); @@ -658,14 +684,11 @@ COMMENT ON TABLE merchant_template IS 'template used by the merchant (may be incomplete, frontend can override)'; COMMENT ON COLUMN merchant_template.template_description IS 'Human-readable template description'; -COMMENT ON COLUMN merchant_template.pos_key - IS 'A base64-encoded key of the point-of-sale. It will be use by the TOTP'; -COMMENT ON COLUMN merchant_template.pos_algorithm - IS 'algorithm to used to generate the confirmation code. It is link with the pos_key'; COMMENT ON COLUMN merchant_template.template_contract IS 'The template contract will contains some additional information.'; + CREATE TABLE IF NOT EXISTS merchant_webhook (webhook_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ,merchant_serial BIGINT NOT NULL diff --git a/src/backenddb/merchantdb_helper.c b/src/backenddb/merchantdb_helper.c index ee52cccc..4ae75020 100644 --- a/src/backenddb/merchantdb_helper.c +++ b/src/backenddb/merchantdb_helper.c @@ -42,7 +42,7 @@ TALER_MERCHANTDB_template_details_free ( struct TALER_MERCHANTDB_TemplateDetails *tp) { GNUNET_free (tp->template_description); - GNUNET_free (tp->pos_key); + GNUNET_free (tp->otp_id); json_decref (tp->template_contract); } diff --git a/src/backenddb/pg_delete_otp.c b/src/backenddb/pg_delete_otp.c new file mode 100644 index 00000000..7d32b3fb --- /dev/null +++ b/src/backenddb/pg_delete_otp.c @@ -0,0 +1,55 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_delete_otp.c + * @brief Implementation of the delete_otp function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_delete_otp.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TMH_PG_delete_otp (void *cls, + const char *instance_id, + const char *otp_id) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (otp_id), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "delete_otp", + "DELETE" + " FROM merchant_otp_devices" + " WHERE merchant_otp_devices.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_otp_devices.template_id=$2"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "delete_otp", + params); +} + diff --git a/src/backenddb/pg_delete_otp.h b/src/backenddb/pg_delete_otp.h new file mode 100644 index 00000000..40e67e8a --- /dev/null +++ b/src/backenddb/pg_delete_otp.h @@ -0,0 +1,43 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_delete_otp.h + * @brief implementation of the delete_otp function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_DELETE_OTP_H +#define PG_DELETE_OTP_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Delete information about an OTP device. + * + * @param cls closure + * @param instance_id instance to delete OTP device of + * @param otp_id otp device to delete + * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + * if template unknown. + */ +enum GNUNET_DB_QueryStatus +TMH_PG_delete_otp (void *cls, + const char *instance_id, + const char *otp_id); + +#endif diff --git a/src/backenddb/pg_delete_template.c b/src/backenddb/pg_delete_template.c new file mode 100644 index 00000000..15531c6b --- /dev/null +++ b/src/backenddb/pg_delete_template.c @@ -0,0 +1,55 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_delete_template.c + * @brief Implementation of the delete_template function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_delete_template.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TMH_PG_delete_template (void *cls, + const char *instance_id, + const char *template_id) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (template_id), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "delete_template", + "DELETE" + " FROM merchant_template" + " WHERE merchant_template.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_template.template_id=$2"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "delete_template", + params); +} + diff --git a/src/backenddb/pg_delete_template.h b/src/backenddb/pg_delete_template.h new file mode 100644 index 00000000..ed0e0cf0 --- /dev/null +++ b/src/backenddb/pg_delete_template.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_delete_template.h + * @brief implementation of the delete_template function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_DELETE_TEMPLATE_H +#define PG_DELETE_TEMPLATE_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Delete information about a template. + * + * @param cls closure + * @param instance_id instance to delete template of + * @param template_id template to delete + * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + * if template unknown. + */ +enum GNUNET_DB_QueryStatus +TMH_PG_delete_template (void *cls, + const char *instance_id, + const char *template_id); + + +#endif diff --git a/src/backenddb/pg_inactivate_account.c b/src/backenddb/pg_inactivate_account.c index 67c39462..7e0d10ae 100644 --- a/src/backenddb/pg_inactivate_account.c +++ b/src/backenddb/pg_inactivate_account.c @@ -38,8 +38,6 @@ TMH_PG_inactivate_account (void *cls, }; check_connection (pg); - /* the merchant instance is implied from the random salt - that is part of the h_wire calculation */ PREPARE (pg, "inactivate_account", "UPDATE merchant_accounts SET" diff --git a/src/backenddb/pg_insert_otp.c b/src/backenddb/pg_insert_otp.c new file mode 100644 index 00000000..feae2230 --- /dev/null +++ b/src/backenddb/pg_insert_otp.c @@ -0,0 +1,74 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_insert_otp.c + * @brief Implementation of the insert_otp function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_insert_otp.h" +#include "pg_helper.h" + + +/** + * Insert details about a particular OTP device. + * + * @param cls closure + * @param instance_id instance to insert OTP device for + * @param otp_id otp identifier of OTP device to insert + * @param td the OTP device details to insert + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_insert_otp (void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td) +{ + struct PostgresClosure *pg = cls; + uint32_t pos32 = (uint32_t) td->otp_algorithm; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (otp_id), + GNUNET_PQ_query_param_string (td->otp_description), + GNUNET_PQ_query_param_string (td->otp_key), + GNUNET_PQ_query_param_uint32 (&pos32), + GNUNET_PQ_query_param_uint64 (&td->otp_ctr), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "insert_otp", + "INSERT INTO merchant_otp_devices" + "(merchant_serial" + ",otp_id" + ",otp_description" + ",otp_key" + ",otp_algorithm" + ",otp_ctr" + ")" + " SELECT merchant_serial," + " $2, $3, $4, $5, $6" + " FROM merchant_instances" + " WHERE merchant_id=$1"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_otp", + params); +} diff --git a/src/backenddb/pg_insert_otp.h b/src/backenddb/pg_insert_otp.h new file mode 100644 index 00000000..70267d37 --- /dev/null +++ b/src/backenddb/pg_insert_otp.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_insert_otp.h + * @brief implementation of the insert_otp function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_INSERT_OTP_H +#define PG_INSERT_OTP_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Insert details about a particular OTP device. + * + * @param cls closure + * @param instance_id instance to insert OTP device for + * @param otp_id otp identifier of OTP device to insert + * @param td the OTP device details to insert + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_insert_otp (void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td); + + +#endif diff --git a/src/backenddb/pg_insert_template.c b/src/backenddb/pg_insert_template.c new file mode 100644 index 00000000..5fc76a1d --- /dev/null +++ b/src/backenddb/pg_insert_template.c @@ -0,0 +1,66 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_insert_template.c + * @brief Implementation of the insert_template function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_insert_template.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TMH_PG_insert_template (void *cls, + const char *instance_id, + const char *template_id, + uint64_t otp_serial_id, + const struct TALER_MERCHANTDB_TemplateDetails *td) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (template_id), + GNUNET_PQ_query_param_string (td->template_description), + (0 == otp_serial_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_uint64 (&otp_serial_id), + TALER_PQ_query_param_json (td->template_contract), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "insert_template", + "INSERT INTO merchant_template" + "(merchant_serial" + ",template_id" + ",template_description" + ",otp_device_id" + ",template_contract" + ")" + " SELECT merchant_serial," + " $2, $3, $4, $5" + " FROM merchant_instances" + " WHERE merchant_id=$1"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_template", + params); +} + diff --git a/src/backenddb/pg_insert_template.h b/src/backenddb/pg_insert_template.h new file mode 100644 index 00000000..fb9c3700 --- /dev/null +++ b/src/backenddb/pg_insert_template.h @@ -0,0 +1,46 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_insert_template.h + * @brief implementation of the insert_template function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_INSERT_TEMPLATE_H +#define PG_INSERT_TEMPLATE_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Insert details about a particular template. + * + * @param cls closure + * @param instance_id instance to insert template for + * @param template_id template identifier of template to insert + * @param otp_serial_id 0 if no OTP device is associated + * @param td the template details to insert + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_insert_template (void *cls, + const char *instance_id, + const char *template_id, + uint64_t otp_serial_id, + const struct TALER_MERCHANTDB_TemplateDetails *td); + +#endif diff --git a/src/backenddb/pg_lookup_instances.c b/src/backenddb/pg_lookup_instances.c index 15137518..323b1957 100644 --- a/src/backenddb/pg_lookup_instances.c +++ b/src/backenddb/pg_lookup_instances.c @@ -92,17 +92,6 @@ prepare (struct PostgresClosure *pg) " merchant_priv" " FROM merchant_keys" " WHERE merchant_serial=$1"); - PREPARE (pg, - "lookup_accounts", - "SELECT" - " h_wire" - ",salt" - ",payto_uri" - ",credit_facade_url" - ",credit_facade_credentials" - ",active" - " FROM merchant_accounts" - " WHERE merchant_serial=$1"); return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } @@ -112,13 +101,9 @@ prepare (struct PostgresClosure *pg) * Find the private key if possible, and invoke the callback. * * @param lic context we are handling - * @param num_accounts length of @a accounts array - * @param accounts information about accounts of the instance in @a lic */ static void -call_with_accounts (struct LookupInstancesContext *lic, - unsigned int num_accounts, - const struct TALER_MERCHANTDB_AccountDetails accounts[]) +call_cb (struct LookupInstancesContext *lic) { struct PostgresClosure *pg = lic->pg; enum GNUNET_DB_QueryStatus qs; @@ -157,91 +142,7 @@ call_with_accounts (struct LookupInstancesContext *lic, &lic->merchant_pub, (0 == qs) ? NULL : &merchant_priv, &lic->is, - &lic->ias, - num_accounts, - accounts); -} - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about accounts. - * - * @param cls of type `struct LookupInstancesContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_accounts_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupInstancesContext *lic = cls; - char *paytos[GNUNET_NZL (num_results)]; - char *facade_urls[GNUNET_NZL (num_results)]; - json_t *credentials[GNUNET_NZL (num_results)]; - struct TALER_MERCHANTDB_AccountDetails accounts[GNUNET_NZL (num_results)]; - - memset (facade_urls, - 0, - sizeof (facade_urls)); - memset (credentials, - 0, - sizeof (credentials)); - /* Note: this memset is completely superfluous, but gcc-11 (and gcc-12) have - a bug creating a warning without it! See #7585 */ - memset (accounts, - 0, - sizeof (accounts)); - for (unsigned int i = 0; i < num_results; i++) - { - struct TALER_MERCHANTDB_AccountDetails *account = &accounts[i]; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("h_wire", - &account->h_wire), - GNUNET_PQ_result_spec_auto_from_type ("salt", - &account->salt), - GNUNET_PQ_result_spec_string ("payto_uri", - &paytos[i]), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("credit_facade_url", - &facade_urls[i]), - NULL), - GNUNET_PQ_result_spec_allow_null ( - TALER_PQ_result_spec_json ("credit_facade_credentials", - &credentials[i]), - NULL), - GNUNET_PQ_result_spec_bool ("active", - &account->active), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - lic->qs = GNUNET_DB_STATUS_HARD_ERROR; - for (unsigned int j = 0; j < i; j++) - { - GNUNET_free (paytos[j]); - GNUNET_free (facade_urls[j]); - json_decref (credentials[j]); - } - return; - } - account->payto_uri = paytos[i]; - } - call_with_accounts (lic, - num_results, - accounts); - for (unsigned int i = 0; i < num_results; i++) - { - GNUNET_free (paytos[i]); - GNUNET_free (facade_urls[i]); - json_decref (credentials[i]); - } + &lic->ias); } @@ -316,10 +217,6 @@ lookup_instances_cb (void *cls, NULL), GNUNET_PQ_result_spec_end }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&lic->instance_serial), - GNUNET_PQ_query_param_end - }; memset (&lic->ias.auth_salt, 0, @@ -337,19 +234,7 @@ lookup_instances_cb (void *cls, return; } lic->is.ut = (enum TALER_KYCLOGIC_KycUserType) ut32; - lic->qs = GNUNET_PQ_eval_prepared_multi_select (lic->pg->conn, - "lookup_accounts", - params, - &lookup_accounts_cb, - lic); - if (0 > lic->qs) - { - /* lookup_accounts_cb() did not run, still notify about the - account-less instance! */ - call_with_accounts (lic, - 0, - NULL); - } + call_cb (lic); GNUNET_PQ_cleanup_result (rs); if (0 > lic->qs) break; diff --git a/src/backenddb/pg_lookup_otp_devices.c b/src/backenddb/pg_lookup_otp_devices.c new file mode 100644 index 00000000..b4a2b569 --- /dev/null +++ b/src/backenddb/pg_lookup_otp_devices.c @@ -0,0 +1,133 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_lookup_otp_devices.c + * @brief Implementation of the lookup_otp_devices function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_lookup_otp_devices.h" +#include "pg_helper.h" + + +/** + * Context used for TMH_PG_lookup_otp_devices(). + */ +struct LookupOtpDeviceContext +{ + /** + * Function to call with the results. + */ + TALER_MERCHANTDB_OtpDeviceCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Did database result extraction fail? + */ + bool extract_failed; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results about otp_device. + * + * @param[in,out] cls of type `struct LookupOtpDeviceContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +lookup_otp_devices_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct LookupOtpDeviceContext *tlc = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + char *otp_device_id; + char *otp_device_description; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("otp_id", + &otp_device_id), + GNUNET_PQ_result_spec_string ("otp_description", + &otp_device_description), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + tlc->extract_failed = true; + return; + } + tlc->cb (tlc->cb_cls, + otp_device_id, + otp_device_description); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TMH_PG_lookup_otp_devices (void *cls, + const char *instance_id, + TALER_MERCHANTDB_OtpDeviceCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct LookupOtpDeviceContext tlc = { + .cb = cb, + .cb_cls = cb_cls, + /* Can be overwritten by the lookup_otp_device_cb */ + .extract_failed = false, + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + check_connection (pg); + PREPARE (pg, + "lookup_otp_devices", + "SELECT" + " otp_id" + ",otp_description" + " FROM merchant_otp_devices" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "lookup_otp_devices", + params, + &lookup_otp_devices_cb, + &tlc); + /* If there was an error inside lookup_otp_device_cb, return a hard error. */ + if (tlc.extract_failed) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} diff --git a/src/backenddb/pg_lookup_otp_devices.h b/src/backenddb/pg_lookup_otp_devices.h new file mode 100644 index 00000000..bb58b70f --- /dev/null +++ b/src/backenddb/pg_lookup_otp_devices.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_lookup_otp_devices.h + * @brief implementation of the lookup_otp_devices function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_LOOKUP_OTP_DEVICES_H +#define PG_LOOKUP_OTP_DEVICES_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Lookup all of the OTP devices the given instance has configured. + * + * @param cls closure + * @param instance_id instance to lookup OTP devices for + * @param cb function to call on all OTP devices found + * @param cb_cls closure for @a cb + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_lookup_otp_devices (void *cls, + const char *instance_id, + TALER_MERCHANTDB_OtpDeviceCallback cb, + void *cb_cls); + + +#endif diff --git a/src/backenddb/pg_lookup_template.c b/src/backenddb/pg_lookup_template.c new file mode 100644 index 00000000..a0326bc8 --- /dev/null +++ b/src/backenddb/pg_lookup_template.c @@ -0,0 +1,98 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_lookup_template.c + * @brief Implementation of the lookup_template function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_lookup_template.h" +#include "pg_helper.h" + + +/** + * Lookup details about a particular template. + * + * @param cls closure + * @param instance_id instance to lookup template for + * @param template_id template to lookup + * @param[out] td set to the template details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_lookup_template (void *cls, + const char *instance_id, + const char *template_id, + struct TALER_MERCHANTDB_TemplateDetails *td) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (template_id), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "lookup_template", + "SELECT" + " mt.template_description" + ",mod.otp_id" + ",mt.template_contract" + " FROM merchant_template mt" + " JOIN merchant_instances mi" + " ON (mi.merchant_serial = mt.merchant_serial)" + " LEFT JOIN merchant_otp_devices mod" + " ON (mod.otp_serial = mt.otp_device_id)" + " WHERE mi.merchant_id=$1" + " AND mt.template_id=$2"); + if (NULL == td) + { + struct GNUNET_PQ_ResultSpec rs_null[] = { + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "lookup_template", + params, + rs_null); + } + else + { + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("template_description", + &td->template_description), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("otp_id", + &td->otp_id), + NULL), + TALER_PQ_result_spec_json ("template_contract", + &td->template_contract), + GNUNET_PQ_result_spec_end + }; + + td->otp_id = NULL; + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "lookup_template", + params, + rs); + } +} + diff --git a/src/backenddb/pg_lookup_template.h b/src/backenddb/pg_lookup_template.h new file mode 100644 index 00000000..44e01b08 --- /dev/null +++ b/src/backenddb/pg_lookup_template.h @@ -0,0 +1,46 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_lookup_template.h + * @brief implementation of the lookup_template function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_LOOKUP_TEMPLATE_H +#define PG_LOOKUP_TEMPLATE_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Lookup details about a particular template. + * + * @param cls closure + * @param instance_id instance to lookup template for + * @param template_id template to lookup + * @param[out] td set to the template details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_lookup_template (void *cls, + const char *instance_id, + const char *template_id, + struct TALER_MERCHANTDB_TemplateDetails *td); + + +#endif diff --git a/src/backenddb/pg_lookup_templates.c b/src/backenddb/pg_lookup_templates.c new file mode 100644 index 00000000..59240994 --- /dev/null +++ b/src/backenddb/pg_lookup_templates.c @@ -0,0 +1,135 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_lookup_templates.c + * @brief Implementation of the lookup_templates function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_lookup_templates.h" +#include "pg_helper.h" + + +/** + * Context used for TMH_PG_lookup_templates(). + */ +struct LookupTemplateContext +{ + /** + * Function to call with the results. + */ + TALER_MERCHANTDB_TemplatesCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Did database result extraction fail? + */ + bool extract_failed; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results about template. + * + * @param[in,out] cls of type `struct LookupTemplateContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +lookup_templates_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct LookupTemplateContext *tlc = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + char *template_id; + char *template_description; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("template_id", + &template_id), + GNUNET_PQ_result_spec_string ("template_description", + &template_description), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + tlc->extract_failed = true; + return; + } + tlc->cb (tlc->cb_cls, + template_id, + template_description); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TMH_PG_lookup_templates (void *cls, + const char *instance_id, + TALER_MERCHANTDB_TemplatesCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct LookupTemplateContext tlc = { + .cb = cb, + .cb_cls = cb_cls, + /* Can be overwritten by the lookup_template_cb */ + .extract_failed = false, + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + check_connection (pg); + PREPARE (pg, + "lookup_templates", + "SELECT" + " template_id" + ",template_description" + " FROM merchant_template" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "lookup_templates", + params, + &lookup_templates_cb, + &tlc); + /* If there was an error inside lookup_template_cb, return a hard error. */ + if (tlc.extract_failed) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} + + diff --git a/src/backenddb/pg_lookup_templates.h b/src/backenddb/pg_lookup_templates.h new file mode 100644 index 00000000..eee5be7e --- /dev/null +++ b/src/backenddb/pg_lookup_templates.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_lookup_templates.h + * @brief implementation of the lookup_templates function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_LOOKUP_TEMPLATES_H +#define PG_LOOKUP_TEMPLATES_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Lookup all of the templates the given instance has configured. + * + * @param cls closure + * @param instance_id instance to lookup template for + * @param cb function to call on all template found + * @param cb_cls closure for @a cb + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_lookup_templates (void *cls, + const char *instance_id, + TALER_MERCHANTDB_TemplatesCallback cb, + void *cb_cls); + +#endif diff --git a/src/backenddb/pg_select_account.c b/src/backenddb/pg_select_account.c new file mode 100644 index 00000000..abf7e9a8 --- /dev/null +++ b/src/backenddb/pg_select_account.c @@ -0,0 +1,79 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_account.c + * @brief Implementation of the select_account function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_select_accounts.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TMH_PG_select_account (void *cls, + const char *id, + const struct TALER_MerchantWireHashP *h_wire, + struct TALER_MERCHANTDB_AccountDetails *ad) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), + GNUNET_PQ_query_param_auto_from_type (h_wire), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("salt", + &ad->salt), + GNUNET_PQ_result_spec_string ("payto_uri", + &ad->payto_uri), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("credit_facade_url", + &ad->credit_facade_url), + NULL), + GNUNET_PQ_result_spec_allow_null ( + TALER_PQ_result_spec_json ("credit_facade_credentials", + &ad->credit_facade_credentials), + NULL), + GNUNET_PQ_result_spec_bool ("active", + &ad->active), + GNUNET_PQ_result_spec_end + }; + + ad->h_wire = *h_wire; + check_connection (pg); + PREPARE (pg, + "select_account", + "SELECT" + " salt" + ",payto_uri" + ",credit_facade_url" + ",credit_facade_credentials" + ",active" + " FROM merchant_accounts" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1) AND " + " h_wire=$2;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "select_account", + params, + rs); +} diff --git a/src/backenddb/pg_select_account.h b/src/backenddb/pg_select_account.h new file mode 100644 index 00000000..d74eaf1e --- /dev/null +++ b/src/backenddb/pg_select_account.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_account.h + * @brief implementation of the select_account function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_SELECT_ACCOUNT_H +#define PG_SELECT_ACCOUNT_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Obtain information about an instance's accounts. + * + * @param cls closure + * @param id identifier of the instance + * @param h_wire wire hash of the account + * @param[out] ad account details returned + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_select_account (void *cls, + const char *id, + const struct TALER_MerchantWireHashP *h_wire, + struct TALER_MERCHANTDB_AccountDetails *ad); + +#endif diff --git a/src/backenddb/pg_select_accounts.c b/src/backenddb/pg_select_accounts.c new file mode 100644 index 00000000..cf15a765 --- /dev/null +++ b/src/backenddb/pg_select_accounts.c @@ -0,0 +1,158 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_accounts.c + * @brief Implementation of the select_accounts function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_select_accounts.h" +#include "pg_helper.h" + + +/** + * Context for select_accounts(). + */ +struct SelectAccountsContext +{ + /** + * Function to call with the results. + */ + TALER_MERCHANTDB_AccountCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Database context. + */ + struct PostgresClosure *pg; + + /** + * Set to the return value on errors. + */ + enum GNUNET_DB_QueryStatus qs; + +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results about accounts. + * + * @param cls of type `struct SelectAccountsContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +select_account_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct SelectAccountsContext *lic = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + char *payto; + char *facade_url = NULL; + json_t *credential = NULL; + struct TALER_MERCHANTDB_AccountDetails acc; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("h_wire", + &acc.h_wire), + GNUNET_PQ_result_spec_auto_from_type ("salt", + &acc.salt), + GNUNET_PQ_result_spec_string ("payto_uri", + &payto), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("credit_facade_url", + &facade_url), + NULL), + GNUNET_PQ_result_spec_allow_null ( + TALER_PQ_result_spec_json ("credit_facade_credentials", + &credential), + NULL), + GNUNET_PQ_result_spec_bool ("active", + &acc.active), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + lic->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; + } + acc.payto_uri = payto; + acc.credit_facade_url = facade_url; + acc.credit_facade_credentials = credential; + lic->cb (lic->cb_cls, + &acc); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TMH_PG_select_accounts (void *cls, + const char *id, + TALER_MERCHANTDB_AccountCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct SelectAccountsContext lic = { + .cb = cb, + .cb_cls = cb_cls, + .pg = pg + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + check_connection (pg); + PREPARE (pg, + "select_accounts", + "SELECT" + " h_wire" + ",salt" + ",payto_uri" + ",credit_facade_url" + ",credit_facade_credentials" + ",active" + " FROM merchant_accounts" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1);"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "select_accounts", + params, + &select_account_cb, + &lic); + if (0 > lic.qs) + return lic.qs; + return qs; +} diff --git a/src/backenddb/pg_select_accounts.h b/src/backenddb/pg_select_accounts.h new file mode 100644 index 00000000..935fada6 --- /dev/null +++ b/src/backenddb/pg_select_accounts.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_accounts.h + * @brief implementation of the select_accounts function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_SELECT_ACCOUNTS_H +#define PG_SELECT_ACCOUNTS_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Obtain information about an instance's accounts. + * + * @param cls closure + * @param id identifier of the instance + * @param cb function to call on each account + * @param cb_cls closure for @a cb + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_select_accounts (void *cls, + const char *id, + TALER_MERCHANTDB_AccountCallback cb, + void *cb_cls); + +#endif diff --git a/src/backenddb/pg_select_otp.c b/src/backenddb/pg_select_otp.c new file mode 100644 index 00000000..f105488a --- /dev/null +++ b/src/backenddb/pg_select_otp.c @@ -0,0 +1,91 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_otp.c + * @brief Implementation of the select_otp function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_select_otp.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TMH_PG_select_otp (void *cls, + const char *instance_id, + const char *otp_id, + struct TALER_MERCHANTDB_OtpDeviceDetails *td) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (otp_id), + GNUNET_PQ_query_param_end + }; + + PREPARE (pg, + "select_otp", + "SELECT" + " otp_description" + ",otp_ctr" + ",otp_key" + ",otp_algorithm" + " FROM merchant_otp_devices" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND merchant_otp_devices.otp_id=$2"); + if (NULL == td) + { + struct GNUNET_PQ_ResultSpec rs_null[] = { + GNUNET_PQ_result_spec_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "select_otp", + params, + rs_null); + } + else + { + uint32_t pos32; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("otp_description", + &td->otp_description), + GNUNET_PQ_result_spec_uint64 ("otp_ctr", + &td->otp_ctr), + GNUNET_PQ_result_spec_string ("otp_key", + &td->otp_key), + GNUNET_PQ_result_spec_uint32 ("otp_algorithm", + &pos32), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; + + check_connection (pg); + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "select_otp", + params, + rs); + td->otp_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos32; + return qs; + } +} + diff --git a/src/backenddb/pg_select_otp.h b/src/backenddb/pg_select_otp.h new file mode 100644 index 00000000..e015cf6b --- /dev/null +++ b/src/backenddb/pg_select_otp.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_otp.h + * @brief implementation of the select_otp function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_SELECT_OTP_H +#define PG_SELECT_OTP_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Lookup details about an OTP device. + * + * @param cls closure + * @param instance_id instance to lookup template for + * @param otp_id OTP device to lookup + * @param[out] td set to the OTP device details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_select_otp (void *cls, + const char *instance_id, + const char *otp_id, + struct TALER_MERCHANTDB_OtpDeviceDetails *td); + +#endif diff --git a/src/backenddb/pg_select_otp_serial.c b/src/backenddb/pg_select_otp_serial.c new file mode 100644 index 00000000..c2011aad --- /dev/null +++ b/src/backenddb/pg_select_otp_serial.c @@ -0,0 +1,61 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_otp_serial.c + * @brief Implementation of the select_otp_serial function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_select_otp_serial.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TMH_PG_select_otp_serial (void *cls, + const char *instance_id, + const char *otp_id, + uint64_t *serial) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (otp_id), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("otp_serial", + serial), + GNUNET_PQ_result_spec_end + }; + + check_connection (pg); + PREPARE (pg, + "select_otp_serial", + "SELECT" + " otp_serial" + " FROM merchant_otp_devices" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND merchant_otp_devices.otp_id=$2"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "select_otp_serial", + params, + rs); +} diff --git a/src/backenddb/pg_select_otp_serial.h b/src/backenddb/pg_select_otp_serial.h new file mode 100644 index 00000000..46d128ae --- /dev/null +++ b/src/backenddb/pg_select_otp_serial.h @@ -0,0 +1,43 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_otp_serial.h + * @brief implementation of the select_otp_serial function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_SELECT_OTP_SERIAL_H +#define PG_SELECT_OTP_SERIAL_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Lookup serial number of an OTP device. + * + * @param cls closure + * @param instance_id instance to lookup template for + * @param otp_id OTP device to lookup + * @param[out] serial set to the OTP device serial number * @return database result code + */ +enum GNUNET_DB_QueryStatus +TMH_PG_select_otp_serial (void *cls, + const char *instance_id, + const char *otp_id, + uint64_t *serial); + +#endif diff --git a/src/backenddb/pg_update_account.c b/src/backenddb/pg_update_account.c index 3e36596d..7458c095 100644 --- a/src/backenddb/pg_update_account.c +++ b/src/backenddb/pg_update_account.c @@ -30,19 +30,20 @@ enum GNUNET_DB_QueryStatus TMH_PG_update_account ( void *cls, const char *id, - const struct TALER_MERCHANTDB_AccountDetails *account_details) + const struct TALER_MerchantWireHashP *h_wire, + const char *credit_facade_url, + const json_t *credit_facade_credentials) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (id), - GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire), - NULL ==account_details->credit_facade_url + GNUNET_PQ_query_param_auto_from_type (h_wire), + NULL == credit_facade_url ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (account_details->credit_facade_url), - NULL == account_details->credit_facade_credentials + : GNUNET_PQ_query_param_string (credit_facade_url), + NULL == credit_facade_credentials ? GNUNET_PQ_query_param_null () - : TALER_PQ_query_param_json (account_details->credit_facade_credentials), - GNUNET_PQ_query_param_bool (account_details->active), + : TALER_PQ_query_param_json (credit_facade_credentials), GNUNET_PQ_query_param_end }; @@ -52,7 +53,6 @@ TMH_PG_update_account ( "UPDATE merchant_accounts SET" " credit_facade_url=$3" ",credit_facade_credentials=COALESCE($4,credit_facade_credentials)" - ",active=$5" " WHERE h_wire=$2" " AND merchant_serial=" " (SELECT merchant_serial" diff --git a/src/backenddb/pg_update_account.h b/src/backenddb/pg_update_account.h index 52b476d9..794b99d8 100644 --- a/src/backenddb/pg_update_account.h +++ b/src/backenddb/pg_update_account.h @@ -31,14 +31,18 @@ * * @param cls closure * @param id identifier of the instance - * @param account_details details about the account + * @param h_wire which account to update + * @param credit_facade_url new facade URL, can be NULL + * @param credit_facade_credentials new credentials, can be NULL to keep previous credentials * @return database result code */ enum GNUNET_DB_QueryStatus TMH_PG_update_account ( void *cls, const char *id, - const struct TALER_MERCHANTDB_AccountDetails *account_details); + const struct TALER_MerchantWireHashP *h_wire, + const char *credit_facade_url, + const json_t *credit_facade_credentials); #endif diff --git a/src/backenddb/pg_update_instance.c b/src/backenddb/pg_update_instance.c index 0f44c833..228e2031 100644 --- a/src/backenddb/pg_update_instance.c +++ b/src/backenddb/pg_update_instance.c @@ -26,6 +26,7 @@ #include "pg_update_instance.h" #include "pg_helper.h" + enum GNUNET_DB_QueryStatus TMH_PG_update_instance (void *cls, const struct TALER_MERCHANTDB_InstanceSettings *is) @@ -40,7 +41,8 @@ TMH_PG_update_instance (void *cls, GNUNET_PQ_query_param_bool (is->use_stefan), GNUNET_PQ_query_param_relative_time ( &is->default_wire_transfer_delay), - GNUNET_PQ_query_param_relative_time (&is->default_pay_delay), + GNUNET_PQ_query_param_relative_time ( + &is->default_pay_delay), (NULL == is->website) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (is->website), diff --git a/src/backenddb/pg_update_otp.c b/src/backenddb/pg_update_otp.c new file mode 100644 index 00000000..8f6c7176 --- /dev/null +++ b/src/backenddb/pg_update_otp.c @@ -0,0 +1,76 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_update_otp.c + * @brief Implementation of the update_otp function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_update_otp.h" +#include "pg_helper.h" + + +/** + * Update details about a particular OTP device. + * + * @param cls closure + * @param instance_id instance to update OTP device for + * @param otp_id OTP device to update + * @param td update to the OTP device details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template + * does not yet exist. + */ +enum GNUNET_DB_QueryStatus +TMH_PG_update_otp (void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td) +{ + struct PostgresClosure *pg = cls; + uint32_t pos32 = (uint32_t) td->otp_algorithm; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (otp_id), + GNUNET_PQ_query_param_string (td->otp_description), + GNUNET_PQ_query_param_uint32 (&pos32), + GNUNET_PQ_query_param_uint64 (&td->otp_ctr), + GNUNET_PQ_query_param_string (td->otp_key), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "update_otp", + "UPDATE merchant_otp_devices SET" + " otp_description=$3" + ",otp_algorithm=$4" + ",otp_ctr=$5" + ",otp_key=$6" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND otp_id=$2"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "update_otp", + params); +} + + diff --git a/src/backenddb/pg_update_otp.h b/src/backenddb/pg_update_otp.h new file mode 100644 index 00000000..7568608b --- /dev/null +++ b/src/backenddb/pg_update_otp.h @@ -0,0 +1,47 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_update_otp.h + * @brief implementation of the update_otp function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_UPDATE_OTP_H +#define PG_UPDATE_OTP_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Update details about a particular OTP device. + * + * @param cls closure + * @param instance_id instance to update OTP device for + * @param otp_id OTP device to update + * @param td update to the OTP device details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template + * does not yet exist. + */ +enum GNUNET_DB_QueryStatus +TMH_PG_update_otp (void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td); + + +#endif diff --git a/src/backenddb/pg_update_template.c b/src/backenddb/pg_update_template.c new file mode 100644 index 00000000..9f7f37c3 --- /dev/null +++ b/src/backenddb/pg_update_template.c @@ -0,0 +1,85 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_update_template.c + * @brief Implementation of the update_template function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_update_template.h" +#include "pg_helper.h" + + +/** + * Update details about a particular template. + * + * @param cls closure + * @param instance_id instance to update template for + * @param template_id template to update + * @param td update to the template details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template + * does not yet exist. + */ +enum GNUNET_DB_QueryStatus +TMH_PG_update_template (void *cls, + const char *instance_id, + const char *template_id, + const struct TALER_MERCHANTDB_TemplateDetails *td) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (template_id), + GNUNET_PQ_query_param_string (td->template_description), + (NULL == td->otp_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string (td->otp_id), + TALER_PQ_query_param_json (td->template_contract), + GNUNET_PQ_query_param_end + }; + + check_connection (pg); + PREPARE (pg, + "update_template", + "WITH mid AS (" + " SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + ",otp AS (" + " SELECT otp_serial" + " FROM merchant_otp_devices" + " JOIN mid USING (merchant_serial)" + " WHERE otp_id=$4)" + "UPDATE merchant_template SET" + " template_description=$3" + ",otp_device_id=" + " COALESCE((SELECT otp_serial" + " FROM otp), NULL)" + ",template_contract=$5" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM mid)" + " AND template_id=$2"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "update_template", + params); +} + + diff --git a/src/backenddb/pg_update_template.h b/src/backenddb/pg_update_template.h new file mode 100644 index 00000000..26b932a2 --- /dev/null +++ b/src/backenddb/pg_update_template.h @@ -0,0 +1,46 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_update_template.h + * @brief implementation of the update_template function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_UPDATE_TEMPLATE_H +#define PG_UPDATE_TEMPLATE_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Update details about a particular template. + * + * @param cls closure + * @param instance_id instance to update template for + * @param template_id template to update + * @param td update to the template details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template + * does not yet exist. + */ +enum GNUNET_DB_QueryStatus +TMH_PG_update_template (void *cls, + const char *instance_id, + const char *template_id, + const struct TALER_MERCHANTDB_TemplateDetails *td); + +#endif diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 70a3e644..c4074c98 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -31,6 +31,11 @@ #include <taler/taler_mhd_lib.h> #include "taler_merchantdb_plugin.h" #include "pg_helper.h" +#include "pg_insert_otp.h" +#include "pg_delete_otp.h" +#include "pg_update_otp.h" +#include "pg_select_otp.h" +#include "pg_select_otp_serial.h" #include "pg_insert_account.h" #include "pg_update_account.h" #include "pg_lookup_instances.h" @@ -43,6 +48,7 @@ #include "pg_insert_exchange_account.h" #include "pg_lookup_reserves.h" #include "pg_lookup_instance_auth.h" +#include "pg_lookup_otp_devices.h" #include "pg_update_transfer_status.h" #include "pg_insert_instance.h" #include "pg_account_kyc_set_status.h" @@ -72,6 +78,11 @@ #include "pg_insert_contract_terms.h" #include "pg_update_contract_terms.h" #include "pg_delete_contract_terms.h" +#include "pg_delete_template.h" +#include "pg_insert_template.h" +#include "pg_update_template.h" +#include "pg_lookup_templates.h" +#include "pg_lookup_template.h" #include "pg_lookup_deposits.h" #include "pg_insert_exchange_signkey.h" #include "pg_insert_deposit.h" @@ -86,6 +97,8 @@ #include "pg_select_exchange_keys.h" #include "pg_insert_deposit_to_transfer.h" #include "pg_increase_refund.h" +#include "pg_select_account.h" +#include "pg_select_accounts.h" #include "pg_insert_transfer.h" #include "pg_insert_transfer_details.h" #include "pg_store_wire_fee_by_exchange.h" @@ -2309,283 +2322,6 @@ postgres_insert_pickup_blind_signature ( /** - * Delete information about a template. - * - * @param cls closure - * @param instance_id instance to delete template of - * @param template_id template to delete - * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS - * if template unknown. - */ -static enum GNUNET_DB_QueryStatus -postgres_delete_template (void *cls, - const char *instance_id, - const char *template_id) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "delete_template", - params); -} - - -/** - * Insert details about a particular template. - * - * @param cls closure - * @param instance_id instance to insert template for - * @param template_id template identifier of template to insert - * @param td the template details to insert - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_template (void *cls, - const char *instance_id, - const char *template_id, - const struct TALER_MERCHANTDB_TemplateDetails *td) -{ - struct PostgresClosure *pg = cls; - uint32_t pos32 = (uint32_t) td->pos_algorithm; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_string (td->template_description), - (NULL == td->pos_key) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (td->pos_key), - GNUNET_PQ_query_param_uint32 (&pos32), - TALER_PQ_query_param_json (td->template_contract), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_template", - params); -} - - -/** - * Update details about a particular template. - * - * @param cls closure - * @param instance_id instance to update template for - * @param template_id template to update - * @param td update to the template details on success, can be NULL - * (in that case we only want to check if the template exists) - * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template - * does not yet exist. - */ -static enum GNUNET_DB_QueryStatus -postgres_update_template (void *cls, - const char *instance_id, - const char *template_id, - const struct TALER_MERCHANTDB_TemplateDetails *td) -{ - struct PostgresClosure *pg = cls; - uint32_t pos32 = (uint32_t) td->pos_algorithm; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_string (td->template_description), - (NULL == td->pos_key) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (td->pos_key), - GNUNET_PQ_query_param_uint32 (&pos32), - TALER_PQ_query_param_json (td->template_contract), - GNUNET_PQ_query_param_end - }; - - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_template", - params); -} - - -/** - * Context used for postgres_lookup_template(). - */ -struct LookupTemplateContext -{ - /** - * Function to call with the results. - */ - TALER_MERCHANTDB_TemplatesCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Did database result extraction fail? - */ - bool extract_failed; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results about template. - * - * @param[in,out] cls of type `struct LookupTemplateContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -lookup_templates_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct LookupTemplateContext *tlc = cls; - - for (unsigned int i = 0; i < num_results; i++) - { - char *template_id; - char *template_description; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("template_id", - &template_id), - GNUNET_PQ_result_spec_string ("template_description", - &template_description), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - tlc->extract_failed = true; - return; - } - tlc->cb (tlc->cb_cls, - template_id, - template_description); - GNUNET_PQ_cleanup_result (rs); - } -} - - -/** - * Lookup all of the templates the given instance has configured. - * - * @param cls closure - * @param instance_id instance to lookup template for - * @param cb function to call on all template found - * @param cb_cls closure for @a cb - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_templates (void *cls, - const char *instance_id, - TALER_MERCHANTDB_TemplatesCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct LookupTemplateContext tlc = { - .cb = cb, - .cb_cls = cb_cls, - /* Can be overwritten by the lookup_template_cb */ - .extract_failed = false, - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "lookup_templates", - params, - &lookup_templates_cb, - &tlc); - /* If there was an error inside lookup_template_cb, return a hard error. */ - if (tlc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Lookup details about a particular template. - * - * @param cls closure - * @param instance_id instance to lookup template for - * @param template_id template to lookup - * @param[out] td set to the template details on success, can be NULL - * (in that case we only want to check if the template exists) - * @return database result code - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_template (void *cls, - const char *instance_id, - const char *template_id, - struct TALER_MERCHANTDB_TemplateDetails *td) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_string (template_id), - GNUNET_PQ_query_param_end - }; - - if (NULL == td) - { - struct GNUNET_PQ_ResultSpec rs_null[] = { - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_template", - params, - rs_null); - } - else - { - uint32_t pos32; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("template_description", - &td->template_description), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("pos_key", - &td->pos_key), - NULL), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_uint32 ("pos_algorithm", - &pos32), - NULL), - TALER_PQ_result_spec_json ("template_contract", - &td->template_contract), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - td->pos_key = NULL; - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "lookup_template", - params, - rs); - td->pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos32; - return qs; - } -} - - -/** * Delete information about a webhook. * * @param cls closure @@ -3747,62 +3483,6 @@ postgres_connect (void *cls) " pickup_serial, $2, $3" " FROM merchant_reward_pickups" " WHERE pickup_id=$1"), - /* for postgres_lookup_templates() */ - GNUNET_PQ_make_prepare ("lookup_templates", - "SELECT" - " template_id" - ",template_description" - " FROM merchant_template" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1"), - /* for postgres_lookup_template() */ - GNUNET_PQ_make_prepare ("lookup_template", - "SELECT" - " template_description" - ",pos_key" - ",pos_algorithm" - ",template_contract" - " FROM merchant_template" - " JOIN merchant_instances" - " USING (merchant_serial)" - " WHERE merchant_instances.merchant_id=$1" - " AND merchant_template.template_id=$2"), - /* for postgres_delete_template() */ - GNUNET_PQ_make_prepare ("delete_template", - "DELETE" - " FROM merchant_template" - " WHERE merchant_template.merchant_serial=" - " (SELECT merchant_serial " - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND merchant_template.template_id=$2"), - /* for postgres_insert_template() */ - GNUNET_PQ_make_prepare ("insert_template", - "INSERT INTO merchant_template" - "(merchant_serial" - ",template_id" - ",template_description" - ",pos_key" - ",pos_algorithm" - ",template_contract" - ")" - " SELECT merchant_serial," - " $2, $3, $4, $5, $6" - " FROM merchant_instances" - " WHERE merchant_id=$1"), - /* for postgres_update_template() */ - GNUNET_PQ_make_prepare ("update_template", - "UPDATE merchant_template SET" - " template_description=$3" - ",pos_key=$4" - ",pos_algorithm=$5" - ",template_contract=$6" - " WHERE merchant_serial=" - " (SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1)" - " AND template_id=$2"), /* for postgres_lookup_webhooks() */ GNUNET_PQ_make_prepare ("lookup_webhooks", "SELECT" @@ -4010,6 +3690,18 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_insert_instance; plugin->insert_account = &TMH_PG_insert_account; + plugin->lookup_otp_devices + = &TMH_PG_lookup_otp_devices; + plugin->delete_template + = &TMH_PG_delete_template; + plugin->insert_template + = &TMH_PG_insert_template; + plugin->update_template + = &TMH_PG_update_template; + plugin->lookup_templates + = &TMH_PG_lookup_templates; + plugin->lookup_template + = &TMH_PG_lookup_template; plugin->update_account = &TMH_PG_update_account; plugin->account_kyc_set_status @@ -4040,6 +3732,16 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_insert_product; plugin->update_product = &TMH_PG_update_product; + plugin->insert_otp + = &TMH_PG_insert_otp; + plugin->delete_otp + = &TMH_PG_delete_otp; + plugin->update_otp + = &TMH_PG_update_otp; + plugin->select_otp + = &TMH_PG_select_otp; + plugin->select_otp_serial + = &TMH_PG_select_otp_serial; plugin->lock_product = &TMH_PG_lock_product; plugin->expire_locks @@ -4117,6 +3819,10 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_update_wirewatch_progress; plugin->select_wirewatch_accounts = &TMH_PG_select_wirewatch_accounts; + plugin->select_account + = &TMH_PG_select_account; + plugin->select_accounts + = &TMH_PG_select_accounts; plugin->lookup_reserves = &TMH_PG_lookup_reserves; plugin->lookup_pending_reserves = &postgres_lookup_pending_reserves; @@ -4151,11 +3857,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_authorize_reward; plugin->insert_pickup = &TMH_PG_insert_pickup; - plugin->lookup_templates = &postgres_lookup_templates; - plugin->lookup_template = &postgres_lookup_template; - plugin->delete_template = &postgres_delete_template; - plugin->insert_template = &postgres_insert_template; - plugin->update_template = &postgres_update_template; plugin->lookup_webhooks = &postgres_lookup_webhooks; plugin->lookup_webhook = &postgres_lookup_webhook; plugin->delete_webhook = &postgres_delete_webhook; diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index 58470786..214bd5f8 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -259,6 +259,19 @@ check_accounts_equal (const struct TALER_MERCHANTDB_AccountDetails *a, } +// FIXME: use this! +void +lookup_accounts_cb (void *cls, + const struct TALER_MERCHANTDB_AccountDetails *account) +{ + const struct TALER_MERCHANTDB_AccountDetails *want = cls; + + GNUNET_assert (0 == + check_accounts_equal (want, + account)); +} + + /** * Called after testing 'lookup_instances'. * @@ -267,54 +280,33 @@ check_accounts_equal (const struct TALER_MERCHANTDB_AccountDetails *a, * @param merchant_priv private key of the instance, NULL if not available * @param is general instance settings * @param ias instance authentication settings - * @param accounts_length length of the @a accounts array - * @param accounts list of accounts of the merchant */ static void lookup_instances_cb (void *cls, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPrivateKeyP *merchant_priv, const struct TALER_MERCHANTDB_InstanceSettings *is, - const struct TALER_MERCHANTDB_InstanceAuthSettings *ias, - unsigned int accounts_length, - const struct TALER_MERCHANTDB_AccountDetails accounts[]) + const struct TALER_MERCHANTDB_InstanceAuthSettings *ias) { struct TestLookupInstances_Closure *cmp = cls; + if (NULL == cmp) return; cmp->results_length += 1; /* Look through the closure and test each instance for equality */ for (unsigned int i = 0; cmp->instances_to_cmp_length > i; ++i) { - int accounts_matching[GNUNET_NZL (accounts_length)]; - bool accounts_match = true; if (0 != check_instances_equal (cmp->instances_to_cmp[i].instance, is)) continue; - if (accounts_length != cmp->instances_to_cmp[i].accounts_length) - continue; - /* Count matches between the accounts found and accounts in cls */ - for (unsigned int j = 0; accounts_length > j; ++j) - accounts_matching[j] = 0; - for (unsigned int j = 0; accounts_length > j; ++j) - { - for (unsigned int k = 0; accounts_length > k; ++k) - { - if (0 == check_accounts_equal (&cmp->instances_to_cmp[i].accounts[j], - &accounts[k])) - accounts_matching[j] += 1; - } - } - /* Each account from the lookup should match with one and only one from cls */ - for (unsigned int j = 0; accounts_length > j; ++j) - if (1 != accounts_matching[j]) - accounts_match = false; - if (true == accounts_match) - cmp->results_matching[i] += 1; + cmp->results_matching[i] += 1; } } + + + /** * Tests @e insert_instance. * @@ -6866,8 +6858,7 @@ make_template (const char *id, { template->id = id; template->template.template_description = "This is a test template"; - template->template.pos_key = NULL; - template->template.pos_algorithm = 0; + template->template.otp_id = NULL; template->template.template_contract = json_array (); GNUNET_assert (NULL != template->template.template_contract); } @@ -6881,7 +6872,7 @@ make_template (const char *id, static void free_template_data (struct TemplateData *template) { - GNUNET_free (template->template.pos_key); + GNUNET_free (template->template.otp_id); json_decref (template->template.template_contract); } @@ -6899,9 +6890,10 @@ check_templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *a, { if ((0 != strcmp (a->template_description, b->template_description)) || - ( (NULL == a->pos_key) && (NULL != b->pos_key)) || - ( (NULL != a->pos_key) && (NULL == b->pos_key)) || - ( (NULL != a->pos_key) && (0 != strcmp (a->pos_key, b->pos_key))) || + ( (NULL == a->otp_id) && (NULL != b->otp_id)) || + ( (NULL != a->otp_id) && (NULL == b->otp_id)) || + ( (NULL != a->otp_id) && (0 != strcmp (a->otp_id, + b->otp_id))) || (1 != json_equal (a->template_contract, b->template_contract))) return 1; @@ -6967,6 +6959,7 @@ test_lookup_template (const struct InstanceData *instance, const struct TemplateData *template) { struct TALER_MERCHANTDB_TemplateDetails lookup_result; + if (0 > plugin->lookup_template (plugin->cls, instance->instance.id, template->id, @@ -7212,8 +7205,23 @@ run_test_templates (struct TestTemplates_Closure *cls) /* Test template update */ cls->templates[0].template.template_description = "This is a test template that has been updated!"; - GNUNET_free (cls->templates[0].template.pos_key); - cls->templates[0].template.pos_key = GNUNET_strdup ("pos_key"); + GNUNET_free (cls->templates[0].template.otp_id); + cls->templates[0].template.otp_id = GNUNET_strdup ("otp_id"); + { + /* ensure OTP device exists */ + struct TALER_MERCHANTDB_OtpDeviceDetails td = { + .otp_description = "my otp", + .otp_key = "my key", + .otp_algorithm = 1, + .otp_ctr = 42 + }; + GNUNET_assert (0 <= + plugin->insert_otp (plugin->cls, + cls->instance.instance.id, + "otp_id", + &td)); + } + GNUNET_assert (0 == json_array_append_new ( cls->templates[0].template.template_contract, diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index c41080be..e92ea3b8 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -524,37 +524,11 @@ typedef void /** - * Information about an account of the merchant. - */ -struct TALER_MERCHANT_AccountConfig -{ - /** - * Payto URI of the account. - */ - const char *payto_uri; - - /** - * Optional credit facade for the account. - * Can be NULL. - */ - const char *credit_facade_url; - - /** - * Credit facade credentials for the account. - * Can be NULL. - */ - json_t *credit_facade_credentials; - -}; - -/** * Setup an new instance in the backend. * * @param ctx the context * @param backend_url HTTP base URL for the backend * @param instance_id identity of the instance to get information about - * @param accounts_length how many bank accounts this instance has - * @param accounts the bank accounts of the merchant instance * @param name name of the merchant instance * @param ut user type of the merchant instance * @param address physical address of the merchant instance @@ -573,8 +547,6 @@ TALER_MERCHANT_instances_post ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const char *instance_id, - unsigned int accounts_length, - const struct TALER_MERCHANT_AccountConfig accounts[], const char *name, enum TALER_KYCLOGIC_KycUserType ut, const json_t *address, @@ -599,126 +571,6 @@ TALER_MERCHANT_instances_post_cancel ( /** - * Handle for a POST /instances/$ID/account operation. - */ -struct TALER_MERCHANT_AccountPostHandle; - - -/** - * Response for a POST /instances/$ID/account operation. - */ -struct TALER_MERCHANT_AccountPostResponse -{ - /** - * HTTP response data - */ - struct TALER_MERCHANT_HttpResponse hr; -}; - - -/** - * Function called with the result of the POST /instances/$ID/account operation. - * - * @param cls closure - * @param par response data - */ -typedef void -(*TALER_MERCHANT_AccountPostCallback)( - void *cls, - const struct TALER_MERCHANT_AccountPostResponse *par); - - -/** - * Setup an new account for an instance in the backend. - * - * @param ctx the context - * @param backend_url HTTP base URL for the backend - * @param account the bank accounts to add to the merchant instance - * @param cb function to call with the response - * @param cb_cls closure for @a config_cb - * @return the instances handle; NULL upon error - */ -struct TALER_MERCHANT_AccountPostHandle * -TALER_MERCHANT_account_post ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_MERCHANT_AccountConfig *account, - TALER_MERCHANT_AccountPostCallback cb, - void *cb_cls); - - -/** - * Cancel /account request. Must not be called by clients after - * the callback was invoked. - * - * @param pah request to cancel. - */ -void -TALER_MERCHANT_account_post_cancel ( - struct TALER_MERCHANT_AccountPostHandle *pah); - - -/** - * Handle for a DELETE /instances/$ID/account/$H_WIRE operation. - */ -struct TALER_MERCHANT_AccountDeleteHandle; - - -/** - * Response for a DELETE /instances/$ID/account operation. - */ -struct TALER_MERCHANT_AccountDeleteResponse -{ - /** - * HTTP response data - */ - struct TALER_MERCHANT_HttpResponse hr; -}; - - -/** - * Function called with the result of the DELETE /instances/$ID/account/$H_WIRE operation. - * - * @param cls closure - * @param par response data - */ -typedef void -(*TALER_MERCHANT_AccountDeleteCallback)( - void *cls, - const struct TALER_MERCHANT_AccountDeleteResponse *par); - - -/** - * Remove bank account from an instance in the backend. - * - * @param ctx the context - * @param backend_url HTTP base URL for the backend - * @param h_wire wire hash of the bank accounts to delete - * @param cb function to call with the response - * @param cb_cls closure for @a config_cb - * @return the instances handle; NULL upon error - */ -struct TALER_MERCHANT_AccountDeleteHandle * -TALER_MERCHANT_account_delete ( - struct GNUNET_CURL_Context *ctx, - const char *backend_url, - const struct TALER_MerchantWireHashP *h_wire, - TALER_MERCHANT_AccountDeleteCallback cb, - void *cb_cls); - - -/** - * Cancel /account request. Must not be called by clients after - * the callback was invoked. - * - * @param pah request to cancel. - */ -void -TALER_MERCHANT_account_delete_cancel ( - struct TALER_MERCHANT_AccountDeleteHandle *pah); - - -/** * Handle for a PATCH /instances/$ID operation. */ struct TALER_MERCHANT_InstancePatchHandle; @@ -744,8 +596,6 @@ typedef void * or base URL of an instance if @a instance_id is NULL) * @param instance_id identity of the instance to modify information about; NULL * if the instance is identified as part of the @a backend_url - * @param accounts_length length of the @a accounts array - * @param accounts the bank accounts of the merchant instance * @param name name of the merchant instance * @param ut user type of the merchant instance * @param address physical address of the merchant instance @@ -763,8 +613,6 @@ TALER_MERCHANT_instance_patch ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const char *instance_id, - unsigned int accounts_length, - const struct TALER_MERCHANT_AccountConfig accounts[static accounts_length], const char *name, enum TALER_KYCLOGIC_KycUserType ut, const json_t *address, @@ -848,39 +696,6 @@ struct TALER_MERCHANT_InstanceGetHandle; /** - * Details about a merchant's bank account. - */ -struct TALER_MERCHANT_Account -{ - /** - * salt used to compute h_wire - */ - struct TALER_WireSaltP salt; - - /** - * payto:// URI of the account. - */ - const char *payto_uri; - - /** - * Credit facade URL of the account. - */ - const char *credit_facade_url; - - /** - * Hash of @e payto_uri and @e salt. - */ - struct TALER_MerchantWireHashP h_wire; - - /** - * true if the account is active, - * false if it is historic. - */ - bool active; -}; - - -/** * Details about an instance. */ struct TALER_MERCHANT_InstanceDetails @@ -944,16 +759,6 @@ struct TALER_MERCHANT_InstanceGetResponse struct { /** - * Length of the @e accounts array. - */ - unsigned int accounts_length; - - /** - * bank accounts of the merchant instance - */ - const struct TALER_MERCHANT_Account *accounts; - - /** * Details about the instance. */ struct TALER_MERCHANT_InstanceDetails details; @@ -1093,6 +898,420 @@ TALER_MERCHANT_instance_delete_cancel ( TALER_MERCHANT_instance_delete_cancel (arg) +/* *************** Accounts **************** */ + +/** + * Handle for a POST /instances/$ID/accounts operation. + */ +struct TALER_MERCHANT_AccountsPostHandle; + + +/** + * Response for a POST /instances/$ID/account operation. + */ +struct TALER_MERCHANT_AccountsPostResponse +{ + /** + * HTTP response data + */ + struct TALER_MERCHANT_HttpResponse hr; + + /** + * Details depending on HTTP status. + */ + union { + + /** + * Details returned on #MHD_HTTP_OK. + */ + struct { + + /** + * Hash of @e payto_uri and @e salt. + */ + struct TALER_MerchantWireHashP h_wire; + + /** + * salt used to compute h_wire + */ + struct TALER_WireSaltP salt; + } ok; + + } details; +}; + + +/** + * Function called with the result of the POST /instances/$ID/accounts operation. + * + * @param cls closure + * @param par response data + */ +typedef void +(*TALER_MERCHANT_AccountsPostCallback)( + void *cls, + const struct TALER_MERCHANT_AccountsPostResponse *par); + + +/** + * Setup an new account for an instance in the backend. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param payto_uri URI of the bank account as per RFC 8905 + * @param credit_facade_url credit facade for the account, can be NULL + * @param credit_facade_credentials credentials for credit facade, can be NULL + * @param account the bank accounts to add to the merchant instance + * @param cb function to call with the response + * @param cb_cls closure for @a config_cb + * @return the instances handle; NULL upon error + */ +struct TALER_MERCHANT_AccountsPostHandle * +TALER_MERCHANT_accounts_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountsPostCallback cb, + void *cb_cls); + + +/** + * Cancel POST /accounts request. Must not be called by clients after + * the callback was invoked. + * + * @param pah request to cancel. + */ +void +TALER_MERCHANT_accounts_post_cancel ( + struct TALER_MERCHANT_AccountsPostHandle *pah); + + +/** + * Handle for a GET /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountGetHandle; + + +/** + * Details about a merchant's bank account. + */ +struct TALER_MERCHANT_AccountDetails +{ + /** + * salt used to compute h_wire + */ + struct TALER_WireSaltP salt; + + /** + * payto:// URI of the account. + */ + const char *payto_uri; + + /** + * Credit facade URL of the account. + */ + const char *credit_facade_url; + + /** + * Hash of @e payto_uri and @e salt. + */ + struct TALER_MerchantWireHashP h_wire; + + /** + * true if the account is active, + * false if it is historic. + */ + bool active; +}; + + +/** + * Response returned with details about an account. + */ +struct TALER_MERCHANT_AccountGetResponse +{ + /** + * HTTP response data + */ + struct TALER_MERCHANT_HttpResponse hr; + + union + { + + /** + * Data returned on #MHD_HTTP_OK. + */ + struct + { + + /** + * bank accounts of the merchant instance + */ + struct TALER_MERCHANT_AccountDetails ad; + + } ok; + } + details; +}; + + +/** + * Function called with the result of the GET /instances/$ID/accounts/$H_WIRE operation. + * + * @param cls closure + * @param igr response details + */ +typedef void +(*TALER_MERCHANT_AccountGetCallback)( + void *cls, + const struct TALER_MERCHANT_AccountGetResponse *igr); + + +/** + * Get the details on one of the accounts of an instance. Will connect to the + * merchant backend and obtain information about the account. The respective + * information will be passed to the @a cb once available. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param instance_id identity of the instance to get information about + * @param h_wire hash of the wire details + * @param cb function to call with the + * backend's instances information + * @param cb_cls closure for @a config_cb + * @return the instances handle; NULL upon error + */ +struct TALER_MERCHANT_AccountGetHandle * +TALER_MERCHANT_account_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const struct TALER_MerchantWireHashP *h_wire, + TALER_MERCHANT_AccountGetCallback cb, + void *cb_cls); + + +/** + * Cancel GET /accounts/$H_WIRE request. Must not be called by clients after + * the callback was invoked. + * + * @param igh request to cancel. + */ +void +TALER_MERCHANT_account_get_cancel ( + struct TALER_MERCHANT_AccountGetHandle *igh); + + +/** + * Handle for a GET /accounts operation. + */ +struct TALER_MERCHANT_AccountsGetHandle; + +/** + * Individual account (minimal information + * returned via GET /accounts). + */ +struct TALER_MERCHANT_AccountEntry +{ + /** + * account payto URI. + */ + const char *payto_uri; + + /** + * Hash of @e payto_uri and salt. + */ + struct TALER_MerchantWireHashP h_wire; + +}; + + +/** + * Response to a GET /accounts operation. + */ +struct TALER_MERCHANT_AccountsGetResponse +{ + /** + * HTTP response details + */ + struct TALER_MERCHANT_HttpResponse hr; + + /** + * Details depending on status. + */ + union + { + /** + * Details if status is #MHD_HTTP_OK. + */ + struct + { + /** + * length of the @e accounts array + */ + unsigned int accounts_length; + + /** + * array of accounts the requested instance offers + */ + const struct TALER_MERCHANT_AccountEntry *accounts; + } ok; + } details; +}; + + +/** + * Function called with the result of the GET /accounts operation. + * + * @param cls closure + * @param tgr response details + */ +typedef void +(*TALER_MERCHANT_AccountsGetCallback)( + void *cls, + const struct TALER_MERCHANT_AccountsGetResponse *tgr); + + +/** + * Make a GET /accounts request. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param cb function to call with the backend information + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_AccountsGetHandle * +TALER_MERCHANT_accounts_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_AccountsGetCallback cb, + void *cb_cls); + + +/** + * Cancel GET /accounts operation. + * + * @param tgh operation to cancel + */ +void +TALER_MERCHANT_accounts_get_cancel ( + struct TALER_MERCHANT_AccountsGetHandle *tgh); + + +/** + * Handle for a PATCH /account operation. + */ +struct TALER_MERCHANT_AccountPatchHandle; + + +/** + * Function called with the result of the PATCH /account operation. + * + * @param cls closure + * @param hr HTTP response details + */ +typedef void +(*TALER_MERCHANT_AccountPatchCallback)( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr); + + +/** + * Make a PATCH /accounts/$H_WIRE request to update account details. Cannot be used to change the payto URI or the salt. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param h_wire identifies the account to patch + * @param credit_facade_url credit facade for the account, can be NULL + * @param credit_facade_credentials credentials for credit facade, can be NULL + * @param cb function to call with the backend's result + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_AccountPatchHandle * +TALER_MERCHANT_account_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountPatchCallback cb, + void *cb_cls); + + +/** + * Cancel PATCH /accounts/$H_WIRE operation. + * + * @param[in] tph operation to cancel + */ +void +TALER_MERCHANT_account_patch_cancel ( + struct TALER_MERCHANT_AccountPatchHandle *tph); + + +/** + * Handle for a DELETE /instances/$ID/account/$H_WIRE operation. + */ +struct TALER_MERCHANT_AccountDeleteHandle; + + +/** + * Response for a DELETE /instances/$ID/account operation. + */ +struct TALER_MERCHANT_AccountDeleteResponse +{ + /** + * HTTP response data + */ + struct TALER_MERCHANT_HttpResponse hr; +}; + + +/** + * Function called with the result of the DELETE /instances/$ID/account/$H_WIRE operation. + * + * @param cls closure + * @param par response data + */ +typedef void +(*TALER_MERCHANT_AccountDeleteCallback)( + void *cls, + const struct TALER_MERCHANT_AccountDeleteResponse *par); + + +/** + * Remove bank account from an instance in the backend. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param h_wire wire hash of the bank accounts to delete + * @param cb function to call with the response + * @param cb_cls closure for @a config_cb + * @return the instances handle; NULL upon error + */ +struct TALER_MERCHANT_AccountDeleteHandle * +TALER_MERCHANT_account_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + TALER_MERCHANT_AccountDeleteCallback cb, + void *cb_cls); + + +/** + * Cancel /account request. Must not be called by clients after + * the callback was invoked. + * + * @param pah request to cancel. + */ +void +TALER_MERCHANT_account_delete_cancel ( + struct TALER_MERCHANT_AccountDeleteHandle *pah); + + /* ********************* /products *********************** */ @@ -4922,6 +5141,364 @@ TALER_MERCHANT_kyc_get_cancel ( struct TALER_MERCHANT_KycGetHandle *kyc); +/* ********************* /otp-devices *********************** */ + + +/** + * Handle for a GET /otp-devices operation. + */ +struct TALER_MERCHANT_OtpDevicesGetHandle; + +/** + * Individual OTP device (minimal information + * returned via GET /otp-devices). + */ +struct TALER_MERCHANT_OtpDeviceEntry +{ + /** + * OTP device identifier. + */ + const char *otp_device_id; + + /** + * OTP device description. + */ + const char *device_description; + +}; + + +/** + * Response to a GET /otp-devices operation. + */ +struct TALER_MERCHANT_OtpDevicesGetResponse +{ + /** + * HTTP response details + */ + struct TALER_MERCHANT_HttpResponse hr; + + /** + * Details depending on status. + */ + union + { + /** + * Details if status is #MHD_HTTP_OK. + */ + struct + { + /** + * length of the @e otp_devices array + */ + unsigned int otp_devices_length; + + /** + * array of otp_devices the requested instance offers + */ + const struct TALER_MERCHANT_OtpDeviceEntry *otp_devices; + } ok; + } details; +}; + + +/** + * Function called with the result of the GET /otp-devices operation. + * + * @param cls closure + * @param tgr response details + */ +typedef void +(*TALER_MERCHANT_OtpDevicesGetCallback)( + void *cls, + const struct TALER_MERCHANT_OtpDevicesGetResponse *tgr); + + +/** + * Make a GET /otp-devices request. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param cb function to call with the backend information + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_OtpDevicesGetHandle * +TALER_MERCHANT_otp_devices_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_OtpDevicesGetCallback cb, + void *cb_cls); + + +/** + * Cancel GET /otp-devices operation. + * + * @param tgh operation to cancel + */ +void +TALER_MERCHANT_otp_devices_get_cancel ( + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh); + + +/** + * Handle for a GET /otp-device/$ID operation. Gets details + * about a single otp_device. Do not confused with a + * `struct TALER_MERCHANT_OtpDevicesGetHandle`, which + * obtains a list of all otp_devices. + */ +struct TALER_MERCHANT_OtpDeviceGetHandle; + + +/** + * Details in a response to a GET /otp-devices request. + */ +struct TALER_MERCHANT_OtpDeviceGetResponse +{ + /** + * HTTP response details. + */ + struct TALER_MERCHANT_HttpResponse hr; + + /** + * Response details depending on the HTTP status. + */ + union + { + /** + * Information returned if the status was #MHD_HTTP_OK. + */ + struct + { + + /** + * description of the otp_device + */ + const char *otp_device_description; + + /** + * OTP device key. + */ + const char *otp_key; + + /** + * current counter. + */ + uint64_t otp_ctr; + + /** + * OTP algorithm used. + */ + enum TALER_MerchantConfirmationAlgorithm otp_alg; + + } ok; + + } details; + +}; + + +/** + * Function called with the result of the GET /otp-device/$ID operation. + * + * @param cls closure + * @param tgr HTTP response details + */ +typedef void +(*TALER_MERCHANT_OtpDeviceGetCallback)( + void *cls, + const struct TALER_MERCHANT_OtpDeviceGetResponse *tgr); + + +/** + * Make a GET /otp-device/$ID request to get details about an + * individual OTP device. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param otp_device_id identifier of the otp_device to inquire about + * @param cb function to call with the backend's otp_device information + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_OtpDeviceGetHandle * +TALER_MERCHANT_otp_device_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceGetCallback cb, + void *cb_cls); + + +/** + * Cancel GET /otp-devices/$ID operation. + * + * @param tgh operation to cancel + */ +void +TALER_MERCHANT_otp_device_get_cancel ( + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh); + + +/** + * Handle for a POST /otp-devices operation. + */ +struct TALER_MERCHANT_OtpDevicesPostHandle; + + +/** + * Function called with the result of the POST /otp-devices operation. + * + * @param cls closure + * @param hr HTTP response details + */ +typedef void +(*TALER_MERCHANT_OtpDevicesPostCallback)( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr); + + +/** + * Make a POST /otp-devices request to add an OTP device + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param otp_id identifier to use for the OTP device + * @param otp_device_description description of the OTP device + * @param otp_key key of the OTP device + * @param otp_alg OTP algorithm used + * @param otp_ctr counter for counter-based OTP + * @param cb function to call with the backend's result + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_OtpDevicesPostHandle * +TALER_MERCHANT_otp_devices_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm otp_alg, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicesPostCallback cb, + void *cb_cls); + + +/** + * Cancel POST /otp-devices operation. + * + * @param[in] tph operation to cancel + */ +void +TALER_MERCHANT_otp_devices_post_cancel ( + struct TALER_MERCHANT_OtpDevicesPostHandle *tph); + + +/** + * Handle for a PATCH /otp-device operation. + */ +struct TALER_MERCHANT_OtpDevicePatchHandle; + + +/** + * Function called with the result of the PATCH /otp-device operation. + * + * @param cls closure + * @param hr HTTP response details + */ +typedef void +(*TALER_MERCHANT_OtpDevicePatchCallback)( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr); + + +/** + * Make a PATCH /otp-device request to update OTP device details + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param otp_id identifier to use for the OTP device; the OTP device must exist, + * or the transaction will fail with a #MHD_HTTP_NOT_FOUND + * HTTP status code + * @param otp_device_description description of the otp_device + * @param otp_key key of the OTP device + * @param otp_alg OTP algorithm used + * @param otp_ctr counter for counter-based OTP + * @param cb function to call with the backend's result + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_OtpDevicePatchHandle * +TALER_MERCHANT_otp_device_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm otp_alg, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicePatchCallback cb, + void *cb_cls); + + +/** + * Cancel PATCH /otp-device operation. + * + * @param[in] tph operation to cancel + */ +void +TALER_MERCHANT_otp_device_patch_cancel ( + struct TALER_MERCHANT_OtpDevicePatchHandle *tph); + + +/** + * Handle for a DELETE /otp-device/$ID operation. + */ +struct TALER_MERCHANT_OtpDeviceDeleteHandle; + + +/** + * Function called with the result of the DELETE /otp-device/$ID operation. + * + * @param cls closure + * @param hr HTTP response details + */ +typedef void +(*TALER_MERCHANT_OtpDeviceDeleteCallback)( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr); + + +/** + * Make a DELETE /otp-device/$ID request to delete an OTP device. + * + * @param ctx the context + * @param backend_url HTTP base URL for the backend + * @param otp_device_id identifier of the OTP device + * @param cb function to call with the backend's deletion status + * @param cb_cls closure for @a cb + * @return the request handle; NULL upon error + */ +struct TALER_MERCHANT_OtpDeviceDeleteHandle * +TALER_MERCHANT_otp_device_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceDeleteCallback cb, + void *cb_cls); + + +/** + * Cancel DELETE /otp-device/$ID operation. + * + * @param[in] tdh operation to cancel + */ +void +TALER_MERCHANT_otp_device_delete_cancel ( + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh); + + /* ********************* /templates *********************** */ @@ -5053,25 +5630,15 @@ struct TALER_MERCHANT_TemplateGetResponse const char *template_description; /** - * Shared key with the POS + * OTP device ID used by the POS, NULL if none. */ - const char *pos_key; - - /** - * Option that add amount of the order - */ - const enum TALER_MerchantConfirmationAlgorithm pos_alg; + const char *otp_id; /** * Template for the contract. */ const json_t *template_contract; - /** - * Algorithm used to generate confirmations for the merchant. - */ - enum TALER_MerchantConfirmationAlgorithm mca; - } ok; } details; @@ -5146,8 +5713,7 @@ typedef void * @param backend_url HTTP base URL for the backend * @param template_id identifier to use for the template * @param template_description description of the template - * @param pos_key shared key with the POS - * @param mca algorithm used to generate confirmations + * @param otp_id ID of the OTP device, or NULL if OTP is not used * @param template_contract is the contract of the company * @param cb function to call with the backend's result * @param cb_cls closure for @a cb @@ -5159,8 +5725,7 @@ TALER_MERCHANT_templates_post ( const char *backend_url, const char *template_id, const char *template_description, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm mca, + const char *otp_id, const json_t *template_contract, TALER_MERCHANT_TemplatesPostCallback cb, void *cb_cls); @@ -5203,8 +5768,7 @@ typedef void * or the transaction will fail with a #MHD_HTTP_NOT_FOUND * HTTP status code * @param template_description description of the template - * @param pos_key shared key with the POS - * @param mca algorithm used to generate confirmations + * @param otp_id device ID of the OTP device, or NULL if OTP is not used * @param template_contract is the contract of the company * @param cb function to call with the backend's result * @param cb_cls closure for @a cb @@ -5216,8 +5780,7 @@ TALER_MERCHANT_template_patch ( const char *backend_url, const char *template_id, const char *template_description, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm mca, + const char *otp_id, json_t *template_contract, TALER_MERCHANT_TemplatePatchCallback cb, void *cb_cls); diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h index bc888fa7..ded1ac5a 100644 --- a/src/include/taler_merchant_testing_lib.h +++ b/src/include/taler_merchant_testing_lib.h @@ -91,7 +91,6 @@ TALER_TESTING_cmd_merchant_get_instances (const char *label, * @param merchant_url base URL of the merchant serving the * POST /instances request. * @param instance_id the ID of the instance to create - * @param payto_uri payment URI to use * @param http_status expected HTTP response code. * @return the command. */ @@ -99,7 +98,6 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_merchant_post_instances (const char *label, const char *merchant_url, const char *instance_id, - const char *payto_uri, unsigned int http_status); @@ -129,8 +127,6 @@ TALER_TESTING_cmd_merchant_post_instance_auth (const char *label, * @param merchant_url base URL of the merchant serving the * POST /instances request. * @param instance_id the ID of the instance to query - * @param accounts_length length of the @a payto_uris array - * @param payto_uris URIs of the bank accounts of the merchant instance * @param name name of the merchant instance * @param address physical address of the merchant instance * @param jurisdiction jurisdiction of the merchant instance @@ -146,8 +142,6 @@ TALER_TESTING_cmd_merchant_post_instances2 ( const char *label, const char *merchant_url, const char *instance_id, - unsigned int accounts_length, - const char *payto_uris[], const char *name, json_t *address, json_t *jurisdiction, @@ -181,19 +175,39 @@ TALER_TESTING_cmd_merchant_post_account ( /** + * Define a "PATCH /account" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * POST /instances request. + * @param create_account_ref reference to account setup command + * @param credit_facade_url credit facade URL to configure, can be NULL + * @param credit_facade_credentials credit facade credentials to use, can be NULL + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_patch_account ( + const char *label, + const char *merchant_url, + const char *create_account_ref, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + unsigned int http_status); + + +/** * Define a "DELETE /account" CMD. * * @param label command label. - * @param get_instance_ref reference to a GET instance command - * @param payto_uri payto URI of the account to delete, must be in the response of the GET instance command + * @param create_account_ref reference to account setup command * @param http_status expected HTTP response code. * @return the command. */ struct TALER_TESTING_Command TALER_TESTING_cmd_merchant_delete_account ( const char *label, - const char *get_instance_ref, - const char *payto_uri, + const char *create_account_ref, unsigned int http_status); @@ -220,8 +234,6 @@ TALER_TESTING_cmd_merchant_patch_instance ( const char *label, const char *merchant_url, const char *instance_id, - unsigned int payto_uris_length, - const char *payto_uris[], const char *name, json_t *address, json_t *jurisdiction, @@ -252,35 +264,6 @@ TALER_TESTING_cmd_merchant_get_instance (const char *label, /** - * Define a "GET instance" CMD that compares accounts returned. - * - * @param label command label. - * @param merchant_url base URL of the merchant serving the - * GET /instances/$ID request. - * @param instance_id the ID of the instance to query - * @param http_status expected HTTP response code. - * @param instance_reference reference to a "POST /instances" or "PATCH /instances/$ID" CMD - * that will provide what we expect the backend to return to us - * @param active_accounts the accounts the merchant is actively using. - * @param active_accounts_length length of @e active_accounts. - * @param inactive_accounts the accounts the merchant is no longer using. - * @param inactive_accounts_length length of @e inactive_accounts. - * @return the command. - */ -struct TALER_TESTING_Command -TALER_TESTING_cmd_merchant_get_instance2 (const char *label, - const char *merchant_url, - const char *instance_id, - unsigned int http_status, - const char *instance_reference, - const char *active_accounts[], - unsigned int active_accounts_length, - const char *inactive_accounts[], - unsigned int - inactive_accounts_length); - - -/** * Define a "PURGE instance" CMD. * * @param label command label. @@ -1511,6 +1494,117 @@ TALER_TESTING_cmd_merchant_kyc_get ( enum TALER_AmlDecisionState expected_aml_state); +/* ****** OTP devices ******* */ + + +/** + * Define a "POST /otp-devices" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * POST /otps request. + * @param otp_id the ID of the otp device to modify + * @param otp_description description of the otp device + * @param otp_key base32-encoded key to verify the payment + * @param otp_alg is an option that show the amount of the order. it is linked with the @a otp_key + * @param otp_ctr counter to use (if in counter mode) + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_post_otp_devices ( + const char *label, + const char *merchant_url, + const char *otp_id, + const char *otp_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm otp_alg, + uint64_t otp_ctr, + unsigned int http_status); + + +/** + * Define a "PATCH /otp-devices/$ID" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * PATCH /otp-devices request. + * @param otp_id the ID of the otp device to modify + * @param otp_description description of the otp device + * @param otp_key base32-encoded key to verify the payment + * @param otp_alg is an option that show the amount of the order. it is linked with the @a otp_key + * @param otp_ctr counter to use (if in counter mode) + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_patch_otp_device ( + const char *label, + const char *merchant_url, + const char *otp_id, + const char *otp_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm otp_alg, + uint64_t otp_ctr, + unsigned int http_status); + + +/** + * Define a "GET /otp-devices" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * GET /otp-devices request. + * @param http_status expected HTTP response code. + * @param ... NULL-terminated list of labels (const char *) of + * otp (commands) we expect to be returned in the list + * (assuming @a http_code is #MHD_HTTP_OK) + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_otp_devices (const char *label, + const char *merchant_url, + unsigned int http_status, + ...); + + +/** + * Define a "GET otp device" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * GET /otp-devices/$ID request. + * @param otp_id the ID of the otp to query + * @param http_status expected HTTP response code. + * @param otp_reference reference to a "POST /otp-devices" or "PATCH /otp-devices/$ID" CMD + * that will provide what we expect the backend to return to us + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_otp_device (const char *label, + const char *merchant_url, + const char *otp_id, + unsigned int http_status, + const char *otp_reference); + + +/** + * Define a "DELETE otp device" CMD. + * + * @param label command label. + * @param merchant_url base URL of the merchant serving the + * DELETE /otp-devices/$ID request. + * @param otp_id the ID of the otp to query + * @param http_status expected HTTP response code. + * @return the command. + */ +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_delete_otp_device (const char *label, + const char *merchant_url, + const char *otp_id, + unsigned int http_status); + + /* ****** Templates ******* */ /** @@ -1521,8 +1615,7 @@ TALER_TESTING_cmd_merchant_kyc_get ( * POST /templates request. * @param template_id the ID of the template to query * @param template_description description of the template - * @param pos_key base32-encoded key to verify the payment - * @param pos_alg is an option that show the amount of the order. it is linked with the pos_key + * @param otp_id OTP device ID, NULL for none * @param template_contract where the contract of the company is * @param http_status expected HTTP response code. * @return the command. @@ -1533,8 +1626,7 @@ TALER_TESTING_cmd_merchant_post_templates2 ( const char *merchant_url, const char *template_id, const char *template_description, - const char *pos_key, - const enum TALER_MerchantConfirmationAlgorithm pos_alg, + const char *otp_id, json_t *template_contract, unsigned int http_status); @@ -1566,8 +1658,7 @@ TALER_TESTING_cmd_merchant_post_templates (const char *label, * PATCH /template request. * @param template_id the ID of the template to query * @param template_description description of the template - * @param pos_key base32-encoded key to verify the payment - * @param pos_alg is an option that show the amount of the order. it is linked with the pos_key + * @param otp_id OTP device to use * @param template_contract contract of the company * @param http_status expected HTTP response code. * @return the command. @@ -1578,8 +1669,7 @@ TALER_TESTING_cmd_merchant_patch_template ( const char *merchant_url, const char *template_id, const char *template_description, - const char *pos_key, - const enum TALER_MerchantConfirmationAlgorithm pos_alg, + const char *otp_id, json_t *template_contract, unsigned int http_status); @@ -1645,6 +1735,7 @@ TALER_TESTING_cmd_merchant_delete_template (const char *label, * * @param label command label. * @param template_ref label of command that created the template to use + * @param otp_ref label of command that created OTP device we use (or NULL for no OTP) * @param merchant_url base URL of the merchant serving the * POST /using-templates request. * @param using_template_id template ID to use @@ -1659,6 +1750,7 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_merchant_post_using_templates ( const char *label, const char *template_ref, + const char *otp_ref, const char *merchant_url, const char *using_template_id, const char *summary, @@ -1909,8 +2001,10 @@ TALER_TESTING_cmd_checkserver2 (const char *label, op (h_wire, const struct TALER_MerchantWireHashP) \ op (proposal_reference, const char) \ op (template_description, const char) \ - op (template_pos_key, const char) \ - op (template_pos_alg, const enum TALER_MerchantConfirmationAlgorithm) \ + op (otp_device_description, const char) \ + op (otp_id, const char) \ + op (otp_key, const char) \ + op (otp_alg, const enum TALER_MerchantConfirmationAlgorithm) \ op (template_id, const char) \ op (template_contract, const json_t) \ op (event_type, const char) \ diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 1c0cd37c..14dedce5 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -96,20 +96,20 @@ struct TALER_MERCHANTDB_AccountDetails /** * Actual account address as a payto://-URI. */ - const char *payto_uri; + char *payto_uri; /** * Where can the taler-merchant-wirewatch helper * download information about incoming transfers? * NULL if not available. */ - const char *credit_facade_url; + char *credit_facade_url; /** * JSON with credentials to use to access the * @e credit_facade_url. */ - const json_t *credit_facade_credentials; + json_t *credit_facade_credentials; /** * Is the account set for active use in new contracts? @@ -219,9 +219,20 @@ typedef void const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPrivateKeyP *merchant_priv, const struct TALER_MERCHANTDB_InstanceSettings *is, - const struct TALER_MERCHANTDB_InstanceAuthSettings *ias, - unsigned int accounts_length, - const struct TALER_MERCHANTDB_AccountDetails accounts[]); + const struct TALER_MERCHANTDB_InstanceAuthSettings *ias); + + +/** + * Callback invoked with information about a bank account. + * + * @param cls closure + * @param ad details about the account + */ +typedef void +(*TALER_MERCHANTDB_AccountCallback)( + void *cls, + const struct TALER_MERCHANTDB_AccountDetails *ad); + /** * Typically called by `lookup_products`. @@ -311,7 +322,7 @@ struct TALER_MERCHANTDB_ProductDetails /** * Typically called by `lookup_templates`. * - * @param cls a `json_t *` JSON array to build + * @param cls closure * @param template_id ID of the template * @param template_description description of the template */ @@ -322,6 +333,19 @@ typedef void /** + * Typically called by `lookup_otp_devices`. + * + * @param cls closure + * @param otp_id ID of the OTP device + * @param otp_description description of the OTP device + */ +typedef void +(*TALER_MERCHANTDB_OtpDeviceCallback)(void *cls, + const char *otp_id, + const char *otp_description); + + +/** * Details about a template. */ struct TALER_MERCHANTDB_TemplateDetails @@ -332,19 +356,42 @@ struct TALER_MERCHANTDB_TemplateDetails char *template_description; /** - * Base64-encoded key, or NULL. + * In this template contract, we can have additional information. + */ + json_t *template_contract; + + /** + * ID of the OTP device linked to the template, or NULL. */ - char *pos_key; + char *otp_id; +}; + + +/** + * Details about an OTP device. + */ +struct TALER_MERCHANTDB_OtpDeviceDetails +{ /** - * In this template contract, we can have additional information. + * Description of the device. */ - json_t *template_contract; + char *otp_description; + + /** + * Current usage counter value. + */ + uint64_t otp_ctr; + + /** + * Base64-encoded key. + */ + char *otp_key; /** * Algorithm used to compute purchase confirmations. */ - enum TALER_MerchantConfirmationAlgorithm pos_algorithm; + enum TALER_MerchantConfirmationAlgorithm otp_algorithm; }; @@ -1223,14 +1270,52 @@ struct TALER_MERCHANTDB_Plugin * * @param cls closure * @param id identifier of the instance - * @param account_details details about the account to update + * @param h_wire which account to update + * @param credit_facade_url new facade URL, can be NULL + * @param credit_facade_credentials new credentials, can be NULL * @return database result code */ enum GNUNET_DB_QueryStatus (*update_account)( void *cls, const char *id, - const struct TALER_MERCHANTDB_AccountDetails *account_details); + const struct TALER_MerchantWireHashP *h_wire, + const char *credit_facade_url, + const json_t *credit_facade_credentials); + + + /** + * Obtain information about an instance's accounts. + * + * @param cls closure + * @param id identifier of the instance + * @param cb function to call on each account + * @param cb_cls closure for @a cb + * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*select_accounts)( + void *cls, + const char *id, + TALER_MERCHANTDB_AccountCallback cb, + void *cb_cls); + + + /** + * Obtain detailed information about an instance's account. + * + * @param cls closure + * @param id identifier of the instance + * @param h_wire wire hash of the account + * @param[out] ad account details returned + * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*select_account)( + void *cls, + const char *id, + const struct TALER_MerchantWireHashP *h_wire, + struct TALER_MERCHANTDB_AccountDetails *ad); /** @@ -2867,6 +2952,7 @@ struct TALER_MERCHANTDB_Plugin * @param cls closure * @param instance_id instance to insert template for * @param template_id template identifier of template to insert + * @param otp_serial_id 0 if no OTP device is associated * @param td the template details to insert * @return database result code */ @@ -2874,10 +2960,106 @@ struct TALER_MERCHANTDB_Plugin (*insert_template)(void *cls, const char *instance_id, const char *template_id, + uint64_t otp_serial_id, const struct TALER_MERCHANTDB_TemplateDetails *td); /** + * Delete information about an OTP device. + * + * @param cls closure + * @param instance_id instance to delete OTP device of + * @param otp_id otp device to delete + * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + * if template unknown. + */ + enum GNUNET_DB_QueryStatus + (*delete_otp) (void *cls, + const char *instance_id, + const char *otp_id); + + /** + * Insert details about a particular OTP device. + * + * @param cls closure + * @param instance_id instance to insert OTP device for + * @param otp_id otp identifier of OTP device to insert + * @param td the OTP device details to insert + * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*insert_otp) (void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td); + + + /** + * Update details about a particular OTP device. + * + * @param cls closure + * @param instance_id instance to update OTP device for + * @param otp_id OTP device to update + * @param td update to the OTP device details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template + * does not yet exist. + */ + enum GNUNET_DB_QueryStatus + (*update_otp) (void *cls, + const char *instance_id, + const char *otp_id, + const struct TALER_MERCHANTDB_OtpDeviceDetails *td); + + /** + * Lookup all of the OTP devices the given instance has configured. + * + * @param cls closure + * @param instance_id instance to lookup OTP devices for + * @param cb function to call on all OTP devices found + * @param cb_cls closure for @a cb + * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*lookup_otp_devices) (void *cls, + const char *instance_id, + TALER_MERCHANTDB_OtpDeviceCallback cb, + void *cb_cls); + + + /** + * Lookup details about an OTP device. + * + * @param cls closure + * @param instance_id instance to lookup template for + * @param otp_id OTP device to lookup + * @param[out] td set to the OTP device details on success, can be NULL + * (in that case we only want to check if the template exists) + * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*select_otp) (void *cls, + const char *instance_id, + const char *otp_id, + struct TALER_MERCHANTDB_OtpDeviceDetails *td); + + + /** + * Lookup serial number of an OTP device. + * + * @param cls closure + * @param instance_id instance to lookup template for + * @param otp_id OTP device to lookup + * @param[out] serial set to the OTP device serial number * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*select_otp_serial) (void *cls, + const char *instance_id, + const char *otp_id, + uint64_t *serial); + + + /** * Update details about a particular template. * * @param cls closure diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index a696f3ea..41cd6674 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -19,16 +19,21 @@ libtalermerchant_la_SOURCES = \ merchant_api_delete_account.c \ merchant_api_delete_instance.c \ merchant_api_delete_order.c \ + merchant_api_delete_otp_device.c \ merchant_api_delete_product.c \ merchant_api_delete_reserve.c \ merchant_api_delete_template.c \ merchant_api_delete_transfer.c \ merchant_api_delete_webhook.c \ + merchant_api_get_account.c \ + merchant_api_get_accounts.c \ merchant_api_get_config.c \ merchant_api_get_instance.c \ merchant_api_get_instances.c \ merchant_api_get_kyc.c \ merchant_api_get_orders.c \ + merchant_api_get_otp_device.c \ + merchant_api_get_otp_devices.c \ merchant_api_get_product.c \ merchant_api_get_products.c \ merchant_api_get_reserve.c \ @@ -42,8 +47,10 @@ libtalermerchant_la_SOURCES = \ merchant_api_lock_product.c \ merchant_api_merchant_get_order.c \ merchant_api_merchant_get_reward.c \ + merchant_api_patch_account.c \ merchant_api_patch_instance.c \ merchant_api_patch_order_forget.c \ + merchant_api_patch_otp_device.c \ merchant_api_patch_product.c \ merchant_api_patch_template.c \ merchant_api_patch_webhook.c \ @@ -56,6 +63,7 @@ libtalermerchant_la_SOURCES = \ merchant_api_post_order_paid.c \ merchant_api_post_order_pay.c \ merchant_api_post_order_refund.c \ + merchant_api_post_otp_devices.c \ merchant_api_post_products.c \ merchant_api_post_reserves.c \ merchant_api_post_transfers.c \ diff --git a/src/lib/merchant_api_delete_otp_device.c b/src/lib/merchant_api_delete_otp_device.c new file mode 100644 index 00000000..5397606c --- /dev/null +++ b/src/lib/merchant_api_delete_otp_device.c @@ -0,0 +1,184 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_delete_otp_device.c + * @brief Implementation of the DELETE /otp-devices/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a DELETE /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDeviceDeleteHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDeviceDeleteCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDeviceDeleteHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_delete_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tdh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got DELETE /otp-devices/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + break; + } + tdh->cb (tdh->cb_cls, + &hr); + TALER_MERCHANT_otp_device_delete_cancel (tdh); +} + + +struct TALER_MERCHANT_OtpDeviceDeleteHandle * +TALER_MERCHANT_otp_device_delete ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceDeleteCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh; + + tdh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceDeleteHandle); + tdh->ctx = ctx; + tdh->cb = cb; + tdh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tdh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tdh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tdh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tdh->url); + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tdh->url); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_DELETE)); + tdh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_delete_otp_device_finished, + tdh); + } + return tdh; +} + + +void +TALER_MERCHANT_otp_device_delete_cancel ( + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh) +{ + if (NULL != tdh->job) + GNUNET_CURL_job_cancel (tdh->job); + GNUNET_free (tdh->url); + GNUNET_free (tdh); +} diff --git a/src/lib/merchant_api_get_account.c b/src/lib/merchant_api_get_account.c new file mode 100644 index 00000000..e9a13b33 --- /dev/null +++ b/src/lib/merchant_api_get_account.c @@ -0,0 +1,211 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_account.c + * @brief Implementation of the GET /accounts/$ID request of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /accounts/$ID request. + * + * @param cls the `struct TALER_MERCHANT_AccountGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("salt", + &tgr.details.ok.ad.salt), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("credit_facade_url", + &tgr.details.ok.ad.credit_facade_url), + NULL), + GNUNET_JSON_spec_string ("payto_uri", + &tgr.details.ok.ad.payto_uri), + GNUNET_JSON_spec_fixed_auto ("h_wire", + &tgr.details.ok.ad.h_wire), + GNUNET_JSON_spec_bool ("active", + &tgr.details.ok.ad.active), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_account_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_account_get_cancel (tgh); +} + + +struct TALER_MERCHANT_AccountGetHandle * +TALER_MERCHANT_account_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *instance_id, + const struct TALER_MerchantWireHashP *h_wire, + TALER_MERCHANT_AccountGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_AccountGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char w_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + w_str, + sizeof (w_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/accounts/%s", + w_str); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_account_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_account_get_cancel ( + struct TALER_MERCHANT_AccountGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_accounts.c b/src/lib/merchant_api_get_accounts.c new file mode 100644 index 00000000..9d09463b --- /dev/null +++ b/src/lib/merchant_api_get_accounts.c @@ -0,0 +1,234 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_accounts.c + * @brief Implementation of the GET /accounts request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /accounts operation. + */ +struct TALER_MERCHANT_AccountsGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountsGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse account information from @a ia. + * + * @param ia JSON array (or NULL!) with account data + * @param[in] tgr partially filled response + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_accounts (const json_t *ia, + struct TALER_MERCHANT_AccountsGetResponse *tgr, + struct TALER_MERCHANT_AccountsGetHandle *tgh) +{ + unsigned int tmpl_len = json_array_size (ia); + struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("payto_uri", + &ie->payto_uri), + GNUNET_JSON_spec_fixed_auto ("h_wire", + &ie->h_wire), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.accounts_length = tmpl_len; + tgr->details.ok.accounts = tmpl; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /accounts request. + * + * @param cls the `struct TALER_MERCHANT_AccountsGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_accounts_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountsGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_AccountsGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /accounts response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *accounts; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("accounts", + &accounts), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_accounts (accounts, + &tgr, + tgh)) + { + TALER_MERCHANT_accounts_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_accounts_get_cancel (tgh); +} + + +struct TALER_MERCHANT_AccountsGetHandle * +TALER_MERCHANT_accounts_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_AccountsGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountsGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + tgh->url = TALER_url_join (backend_url, + "private/accounts", + NULL); + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_accounts_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_accounts_get_cancel ( + struct TALER_MERCHANT_AccountsGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_instance.c b/src/lib/merchant_api_get_instance.c index 9cfbcf83..eef95b84 100644 --- a/src/lib/merchant_api_get_instance.c +++ b/src/lib/merchant_api_get_instance.c @@ -93,14 +93,10 @@ handle_get_instance_finished (void *cls, { case MHD_HTTP_OK: { - const json_t *accounts; const char *uts; const json_t *address; const json_t *jurisdiction; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ( - "accounts", - &accounts), GNUNET_JSON_spec_string ( "name", &igr.details.ok.details.name), @@ -128,78 +124,32 @@ handle_get_instance_finished (void *cls, GNUNET_JSON_spec_end () }; - if (GNUNET_OK == + if (GNUNET_OK != GNUNET_JSON_parse (json, spec, NULL, NULL)) { - unsigned int accounts_length = json_array_size (accounts); - struct TALER_MERCHANT_Account aa[GNUNET_NZL (accounts_length)]; - size_t index; - json_t *value; - int ret = GNUNET_OK; - - memset (aa, - 0, - sizeof (aa)); - json_array_foreach (accounts, index, value) - { - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("salt", - &aa[index].salt), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("credit_facade_url", - &aa[index].credit_facade_url), - NULL), - GNUNET_JSON_spec_string ("payto_uri", - &aa[index].payto_uri), - GNUNET_JSON_spec_fixed_auto ("h_wire", - &aa[index].h_wire), - GNUNET_JSON_spec_bool ("active", - &aa[index].active), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (value, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - } - - if (GNUNET_OK == ret) - { - igr.details.ok.details.address = address; - igr.details.ok.details.jurisdiction = jurisdiction; - if (GNUNET_OK != - TALER_KYCLOGIC_kyc_user_type_from_string ( - uts, - &igr.details.ok.details.ut)) - { - GNUNET_break_op (0); - ret = GNUNET_SYSERR; - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - igr.details.ok.accounts_length = accounts_length; - igr.details.ok.accounts = aa; - igh->cb (igh->cb_cls, - &igr); - TALER_MERCHANT_instance_get_cancel (igh); - return; - } + GNUNET_break_op (0); + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + igr.details.ok.details.address = address; + igr.details.ok.details.jurisdiction = jurisdiction; + if (GNUNET_OK != + TALER_KYCLOGIC_kyc_user_type_from_string ( + uts, + &igr.details.ok.details.ut)) + { + GNUNET_break_op (0); + igr.hr.http_status = 0; + igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; } - GNUNET_break_op (0); - igr.hr.http_status = 0; - igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; + igh->cb (igh->cb_cls, + &igr); + TALER_MERCHANT_instance_get_cancel (igh); + return; } case MHD_HTTP_UNAUTHORIZED: igr.hr.ec = TALER_JSON_get_error_code (json); diff --git a/src/lib/merchant_api_get_otp_device.c b/src/lib/merchant_api_get_otp_device.c new file mode 100644 index 00000000..93e065af --- /dev/null +++ b/src/lib/merchant_api_get_otp_device.c @@ -0,0 +1,204 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_otp_device.c + * @brief Implementation of the GET /otp-devices/$ID request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDeviceGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDeviceGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP GET /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDeviceGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_OtpDeviceGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /otp-devices/$ID response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + uint32_t alg32; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_device_description", + &tgr.details.ok.otp_device_description), + GNUNET_JSON_spec_uint32 ("otp_algorithm", + &alg32), + GNUNET_JSON_spec_string ("otp_key", + &tgr.details.ok.otp_key), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_uint64 ("otp_ctr", + &tgr.details.ok.otp_ctr), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK == + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.details.ok.otp_alg = + (enum TALER_MerchantConfirmationAlgorithm) alg32; + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_device_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_NOT_FOUND: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_device_get_cancel (tgh); +} + + +struct TALER_MERCHANT_OtpDeviceGetHandle * +TALER_MERCHANT_otp_device_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + TALER_MERCHANT_OtpDeviceGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tgh->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_otp_device_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_otp_device_get_cancel ( + struct TALER_MERCHANT_OtpDeviceGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_otp_devices.c b/src/lib/merchant_api_get_otp_devices.c new file mode 100644 index 00000000..3e48486a --- /dev/null +++ b/src/lib/merchant_api_get_otp_devices.c @@ -0,0 +1,234 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_get_otp_devices.c + * @brief Implementation of the GET /otp-devices request of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_signatures.h> + + +/** + * Handle for a GET /otp-devices operation. + */ +struct TALER_MERCHANT_OtpDevicesGetHandle +{ + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicesGetCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Parse OTP device information from @a ia. + * + * @param ia JSON array (or NULL!) with otp_device data + * @param[in] tgr partially filled response + * @param tgh operation handle + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +parse_otp_devices (const json_t *ia, + struct TALER_MERCHANT_OtpDevicesGetResponse *tgr, + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) +{ + unsigned int tmpl_len = json_array_size (ia); + struct TALER_MERCHANT_OtpDeviceEntry tmpl[GNUNET_NZL (tmpl_len)]; + size_t index; + json_t *value; + + json_array_foreach (ia, index, value) { + struct TALER_MERCHANT_OtpDeviceEntry *ie = &tmpl[index]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("otp_device_id", + &ie->otp_device_id), + GNUNET_JSON_spec_string ("device_description", + &ie->device_description), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + tgr->details.ok.otp_devices_length = tmpl_len; + tgr->details.ok.otp_devices = tmpl; + tgh->cb (tgh->cb_cls, + tgr); + tgh->cb = NULL; /* just to be sure */ + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /otp-devices request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicesGetHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_get_otp_devices_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh = cls; + const json_t *json = response; + struct TALER_MERCHANT_OtpDevicesGetResponse tgr = { + .hr.http_status = (unsigned int) response_code, + .hr.reply = json + }; + + tgh->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got /otp-devices response with status code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case MHD_HTTP_OK: + { + const json_t *otp_devices; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("otp_devices", + &otp_devices), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + if (GNUNET_OK == + parse_otp_devices (otp_devices, + &tgr, + tgh)) + { + TALER_MERCHANT_otp_devices_get_cancel (tgh); + return; + } + tgr.hr.http_status = 0; + tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + case MHD_HTTP_UNAUTHORIZED: + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + default: + /* unexpected response code */ + tgr.hr.ec = TALER_JSON_get_error_code (json); + tgr.hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) tgr.hr.ec); + break; + } + tgh->cb (tgh->cb_cls, + &tgr); + TALER_MERCHANT_otp_devices_get_cancel (tgh); +} + + +struct TALER_MERCHANT_OtpDevicesGetHandle * +TALER_MERCHANT_otp_devices_get ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + TALER_MERCHANT_OtpDevicesGetCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh; + CURL *eh; + + tgh = GNUNET_new (struct TALER_MERCHANT_OtpDevicesGetHandle); + tgh->ctx = ctx; + tgh->cb = cb; + tgh->cb_cls = cb_cls; + tgh->url = TALER_url_join (backend_url, + "private/otp-devices", + NULL); + if (NULL == tgh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (tgh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting URL '%s'\n", + tgh->url); + eh = TALER_MERCHANT_curl_easy_get_ (tgh->url); + tgh->job = GNUNET_CURL_job_add (ctx, + eh, + &handle_get_otp_devices_finished, + tgh); + return tgh; +} + + +void +TALER_MERCHANT_otp_devices_get_cancel ( + struct TALER_MERCHANT_OtpDevicesGetHandle *tgh) +{ + if (NULL != tgh->job) + GNUNET_CURL_job_cancel (tgh->job); + GNUNET_free (tgh->url); + GNUNET_free (tgh); +} diff --git a/src/lib/merchant_api_get_template.c b/src/lib/merchant_api_get_template.c index 3e4a23a8..9bbcc93a 100644 --- a/src/lib/merchant_api_get_template.c +++ b/src/lib/merchant_api_get_template.c @@ -92,16 +92,13 @@ handle_get_template_finished (void *cls, { case MHD_HTTP_OK: { - uint32_t alg32; const json_t *contract; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("template_description", &tgr.details.ok.template_description), - GNUNET_JSON_spec_uint32 ("pos_algorithm", - &alg32), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("pos_key", - &tgr.details.ok.pos_key), + GNUNET_JSON_spec_string ("otp_id", + &tgr.details.ok.otp_id), NULL), GNUNET_JSON_spec_object_const ("template_contract", &contract), @@ -113,8 +110,6 @@ handle_get_template_finished (void *cls, spec, NULL, NULL)) { - tgr.details.ok.mca = - (enum TALER_MerchantConfirmationAlgorithm) alg32; tgr.details.ok.template_contract = contract; tgh->cb (tgh->cb_cls, &tgr); diff --git a/src/lib/merchant_api_patch_account.c b/src/lib/merchant_api_patch_account.c new file mode 100644 index 00000000..ce0e74d4 --- /dev/null +++ b/src/lib/merchant_api_patch_account.c @@ -0,0 +1,254 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch_account.c + * @brief Implementation of the PATCH /accounts/$ID request + * of the merchant's HTTP API + * @author Priscilla HUANG + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /accounts/$ID operation. + */ +struct TALER_MERCHANT_AccountPatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_AccountPatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /accounts/$ID request. + * + * @param cls the `struct TALER_MERCHANT_AccountPatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_account_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_AccountPatchHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /accounts/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_account_patch_cancel (tph); +} + + +struct TALER_MERCHANT_AccountPatchHandle * +TALER_MERCHANT_account_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const struct TALER_MerchantWireHashP *h_wire, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountPatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_AccountPatchHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("credit_facade_url", + credit_facade_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("credit_facade_credentials", + (json_t *) credit_facade_credentials))); + tph = GNUNET_new (struct TALER_MERCHANT_AccountPatchHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + { + char w_str[sizeof (*h_wire) * 2]; + char *path; + char *end; + + end = GNUNET_STRINGS_data_to_string (h_wire, + sizeof (*h_wire), + w_str, + sizeof (w_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "private/accounts/%s", + w_str); + tph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_patch_account_finished, + tph); + } + return tph; +} + + +void +TALER_MERCHANT_account_patch_cancel ( + struct TALER_MERCHANT_AccountPatchHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_patch_account.c */ diff --git a/src/lib/merchant_api_patch_instance.c b/src/lib/merchant_api_patch_instance.c index 8b4b6205..420cd549 100644 --- a/src/lib/merchant_api_patch_instance.c +++ b/src/lib/merchant_api_patch_instance.c @@ -158,8 +158,6 @@ TALER_MERCHANT_instance_patch ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const char *instance_id, - unsigned int accounts_length, - const struct TALER_MERCHANT_AccountConfig accounts[static accounts_length], const char *name, enum TALER_KYCLOGIC_KycUserType ut, const json_t *address, @@ -171,7 +169,6 @@ TALER_MERCHANT_instance_patch ( void *cb_cls) { struct TALER_MERCHANT_InstancePatchHandle *iph; - json_t *jaccounts; json_t *req_obj; const char *uts; @@ -181,44 +178,7 @@ TALER_MERCHANT_instance_patch ( GNUNET_break (0); return NULL; } - jaccounts = json_array (); - if (NULL == jaccounts) - { - GNUNET_break (0); - return NULL; - } - for (unsigned int i = 0; i<accounts_length; i++) - { - const struct TALER_MERCHANT_AccountConfig *account = &accounts[i]; - json_t *jaccount; - - jaccount = - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ( - "payto_uri", - account->payto_uri), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - "credit_facade_url", - account->credit_facade_url)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ( - "credit_facade_credentials", - accounts->credit_facade_credentials)) - ); - - if (0 != - json_array_append_new (jaccounts, - jaccount)) - { - GNUNET_break (0); - json_decref (jaccounts); - return NULL; - } - } req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("accounts", - jaccounts), GNUNET_JSON_pack_string ("name", name), GNUNET_JSON_pack_string ("user_type", diff --git a/src/lib/merchant_api_patch_otp_device.c b/src/lib/merchant_api_patch_otp_device.c new file mode 100644 index 00000000..322efe7b --- /dev/null +++ b/src/lib/merchant_api_patch_otp_device.c @@ -0,0 +1,252 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_patch_otp_device.c + * @brief Implementation of the PATCH /otp-devices/$ID request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_common.h" +#include "merchant_api_curl_defaults.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a PATCH /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDevicePatchHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicePatchCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + +}; + + +/** + * Function called when we're done processing the + * HTTP PATCH /otp-devices/$ID request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicePatchHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_patch_otp_device_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicePatchHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "PATCH /otp-devices/$ID completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + GNUNET_break_op (0); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_otp_device_patch_cancel (tph); +} + + +struct TALER_MERCHANT_OtpDevicePatchHandle * +TALER_MERCHANT_otp_device_patch ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm mca, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicePatchCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicePatchHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("otp_device_description", + otp_device_description), + GNUNET_JSON_pack_uint64 ("otp_algorithm", + (uint32_t) mca), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_key", + otp_key)), + GNUNET_JSON_pack_uint64 ("otp_ctr", + otp_ctr)); + tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicePatchHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + { + char *path; + + GNUNET_asprintf (&path, + "private/otp-devices/%s", + otp_device_id); + tph->url = TALER_url_join (backend_url, + path, + NULL); + GNUNET_free (path); + } + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + if (GNUNET_OK != + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)) + { + GNUNET_break (0); + curl_easy_cleanup (eh); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + json_decref (req_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_CUSTOMREQUEST, + MHD_HTTP_METHOD_PATCH)); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_patch_otp_device_finished, + tph); + } + return tph; +} + + +void +TALER_MERCHANT_otp_device_patch_cancel ( + struct TALER_MERCHANT_OtpDevicePatchHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_patch_otp_device.c */ diff --git a/src/lib/merchant_api_patch_template.c b/src/lib/merchant_api_patch_template.c index 0922586a..7dfebf9c 100644 --- a/src/lib/merchant_api_patch_template.c +++ b/src/lib/merchant_api_patch_template.c @@ -162,8 +162,7 @@ TALER_MERCHANT_template_patch ( const char *backend_url, const char *template_id, const char *template_description, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm mca, + const char *otp_id, json_t *template_contract, TALER_MERCHANT_TemplatePatchCallback cb, void *cb_cls) @@ -174,11 +173,9 @@ TALER_MERCHANT_template_patch ( req_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("template_description", template_description), - GNUNET_JSON_pack_uint64 ("pos_algorithm", - (uint32_t) mca), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("pos_key", - pos_key)), + GNUNET_JSON_pack_string ("otp_id", + otp_id)), GNUNET_JSON_pack_object_incref ("template_contract", (json_t *) template_contract)); tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle); diff --git a/src/lib/merchant_api_post_account.c b/src/lib/merchant_api_post_account.c index 5ed3f3b3..690aef17 100644 --- a/src/lib/merchant_api_post_account.c +++ b/src/lib/merchant_api_post_account.c @@ -35,9 +35,9 @@ /** - * Handle for a POST /private/account operation. + * Handle for a POST /private/accounts operation. */ -struct TALER_MERCHANT_AccountPostHandle +struct TALER_MERCHANT_AccountsPostHandle { /** @@ -53,7 +53,7 @@ struct TALER_MERCHANT_AccountPostHandle /** * Function to call with the result. */ - TALER_MERCHANT_AccountPostCallback cb; + TALER_MERCHANT_AccountsPostCallback cb; /** * Closure for @a cb. @@ -86,23 +86,43 @@ handle_post_account_finished (void *cls, long response_code, const void *response) { - struct TALER_MERCHANT_AccountPostHandle *aph = cls; + struct TALER_MERCHANT_AccountsPostHandle *aph = cls; const json_t *json = response; - struct TALER_MERCHANT_AccountPostResponse apr = { + struct TALER_MERCHANT_AccountsPostResponse apr = { .hr.http_status = (unsigned int) response_code, .hr.reply = json }; aph->job = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "POST /account completed with response code %u\n", + "POST /accounts completed with response code %u\n", (unsigned int) response_code); switch (response_code) { case 0: apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; - case MHD_HTTP_NO_CONTENT: + case MHD_HTTP_OK: + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_wire", + &apr.details.ok.h_wire), + GNUNET_JSON_spec_fixed_auto ("salt", + &apr.details.ok.salt), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + apr.hr.http_status = 0; + apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + } + } break; case MHD_HTTP_BAD_REQUEST: GNUNET_break_op (0); @@ -147,40 +167,42 @@ handle_post_account_finished (void *cls, } aph->cb (aph->cb_cls, &apr); - TALER_MERCHANT_account_post_cancel (aph); + TALER_MERCHANT_accounts_post_cancel (aph); } -struct TALER_MERCHANT_AccountPostHandle * -TALER_MERCHANT_account_post ( +struct TALER_MERCHANT_AccountsPostHandle * +TALER_MERCHANT_accounts_post ( struct GNUNET_CURL_Context *ctx, const char *backend_url, - const struct TALER_MERCHANT_AccountConfig *account, - TALER_MERCHANT_AccountPostCallback cb, + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + TALER_MERCHANT_AccountsPostCallback cb, void *cb_cls) { - struct TALER_MERCHANT_AccountPostHandle *aph; + struct TALER_MERCHANT_AccountsPostHandle *aph; json_t *req_obj; req_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ( "payto_uri", - account->payto_uri), + payto_uri), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ( "credit_facade_url", - account->credit_facade_url)), + credit_facade_url)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_incref ( "credit_facade_credentials", - account->credit_facade_credentials)) + (json_t *) credit_facade_credentials)) ); - aph = GNUNET_new (struct TALER_MERCHANT_AccountPostHandle); + aph = GNUNET_new (struct TALER_MERCHANT_AccountsPostHandle); aph->ctx = ctx; aph->cb = cb; aph->cb_cls = cb_cls; aph->url = TALER_url_join (backend_url, - "private/account", + "private/accounts", NULL); if (NULL == aph->url) { @@ -211,8 +233,8 @@ TALER_MERCHANT_account_post ( void -TALER_MERCHANT_account_post_cancel ( - struct TALER_MERCHANT_AccountPostHandle *aph) +TALER_MERCHANT_accounts_post_cancel ( + struct TALER_MERCHANT_AccountsPostHandle *aph) { if (NULL != aph->job) { diff --git a/src/lib/merchant_api_post_instances.c b/src/lib/merchant_api_post_instances.c index 77336187..73d36369 100644 --- a/src/lib/merchant_api_post_instances.c +++ b/src/lib/merchant_api_post_instances.c @@ -164,8 +164,6 @@ TALER_MERCHANT_instances_post ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const char *instance_id, - unsigned int accounts_length, - const struct TALER_MERCHANT_AccountConfig accounts[], const char *name, enum TALER_KYCLOGIC_KycUserType ut, const json_t *address, @@ -178,7 +176,6 @@ TALER_MERCHANT_instances_post ( void *cb_cls) { struct TALER_MERCHANT_InstancesPostHandle *iph; - json_t *jaccounts; json_t *req_obj; json_t *auth_obj; const char *uts; @@ -217,44 +214,7 @@ TALER_MERCHANT_instances_post ( GNUNET_break (0); return NULL; } - jaccounts = json_array (); - if (NULL == jaccounts) - { - json_decref (auth_obj); - GNUNET_break (0); - return NULL; - } - for (unsigned int i = 0; i<accounts_length; i++) - { - const struct TALER_MERCHANT_AccountConfig *account = &accounts[i]; - json_t *jaccount = - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ( - "payto_uri", - account->payto_uri), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - "credit_facade_url", - account->credit_facade_url)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ( - "credit_facade_credentials", - account->credit_facade_credentials)) - ); - - if (0 != - json_array_append_new (jaccounts, - jaccount)) - { - GNUNET_break (0); - json_decref (auth_obj); - json_decref (jaccounts); - return NULL; - } - } req_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("accounts", - jaccounts), GNUNET_JSON_pack_string ("id", instance_id), GNUNET_JSON_pack_string ("name", diff --git a/src/lib/merchant_api_post_otp_devices.c b/src/lib/merchant_api_post_otp_devices.c new file mode 100644 index 00000000..456abd09 --- /dev/null +++ b/src/lib/merchant_api_post_otp_devices.c @@ -0,0 +1,237 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with TALER; see the file COPYING.LGPL. + If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant_api_post_otp_devices.c + * @brief Implementation of the POST /otp-devices request + * of the merchant's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchant_service.h" +#include "merchant_api_curl_defaults.h" +#include "merchant_api_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> + + +/** + * Handle for a POST /otp-devices/$ID operation. + */ +struct TALER_MERCHANT_OtpDevicesPostHandle +{ + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_OtpDevicesPostCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; +}; + + +/** + * Function called when we're done processing the + * HTTP POST /otp-devices request. + * + * @param cls the `struct TALER_MERCHANT_OtpDevicesPostHandle` + * @param response_code HTTP response code, 0 on error + * @param response response body, NULL if not in JSON + */ +static void +handle_post_otp_devices_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_MERCHANT_OtpDevicesPostHandle *tph = cls; + const json_t *json = response; + struct TALER_MERCHANT_HttpResponse hr = { + .http_status = (unsigned int) response_code, + .reply = json + }; + + tph->job = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "POST /otp-devices completed with response code %u\n", + (unsigned int) response_code); + switch (response_code) + { + case 0: + hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + break; + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_BAD_REQUEST: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* This should never happen, either us + * or the merchant is buggy (or API version conflict); + * just pass JSON reply to the application */ + break; + case MHD_HTTP_UNAUTHORIZED: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we need to authenticate. */ + break; + case MHD_HTTP_FORBIDDEN: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, merchant says we tried to abort the payment + * after it was successful. We should pass the JSON reply to the + * application */ + break; + case MHD_HTTP_NOT_FOUND: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the + application */ + break; + case MHD_HTTP_CONFLICT: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + hr.ec = TALER_JSON_get_error_code (json); + hr.hint = TALER_JSON_get_error_hint (json); + /* Server had an internal issue; we should retry, + but this API leaves this to the application */ + break; + default: + TALER_MERCHANT_parse_error_details_ (json, + response_code, + &hr); + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d\n", + (unsigned int) response_code, + (int) hr.ec); + GNUNET_break_op (0); + break; + } + tph->cb (tph->cb_cls, + &hr); + TALER_MERCHANT_otp_devices_post_cancel (tph); +} + + +struct TALER_MERCHANT_OtpDevicesPostHandle * +TALER_MERCHANT_otp_devices_post ( + struct GNUNET_CURL_Context *ctx, + const char *backend_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + enum TALER_MerchantConfirmationAlgorithm otp_algorithm, + uint64_t otp_ctr, + TALER_MERCHANT_OtpDevicesPostCallback cb, + void *cb_cls) +{ + struct TALER_MERCHANT_OtpDevicesPostHandle *tph; + json_t *req_obj; + + req_obj = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("otp_device_id", + otp_device_id), + GNUNET_JSON_pack_string ("otp_device_description", + otp_device_description), + GNUNET_JSON_pack_uint64 ("otp_algorithm", + (uint32_t) otp_algorithm), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("otp_key", + otp_key)), + GNUNET_JSON_pack_uint64 ("otp_ctr", + otp_ctr)); + tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicesPostHandle); + tph->ctx = ctx; + tph->cb = cb; + tph->cb_cls = cb_cls; + tph->url = TALER_url_join (backend_url, + "private/otp-devices", + NULL); + if (NULL == tph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + json_decref (req_obj); + GNUNET_free (tph); + return NULL; + } + { + CURL *eh; + + eh = TALER_MERCHANT_curl_easy_get_ (tph->url); + GNUNET_assert (GNUNET_OK == + TALER_curl_easy_post (&tph->post_ctx, + eh, + req_obj)); + json_decref (req_obj); + tph->job = GNUNET_CURL_job_add2 (ctx, + eh, + tph->post_ctx.headers, + &handle_post_otp_devices_finished, + tph); + GNUNET_assert (NULL != tph->job); + } + return tph; +} + + +void +TALER_MERCHANT_otp_devices_post_cancel ( + struct TALER_MERCHANT_OtpDevicesPostHandle *tph) +{ + if (NULL != tph->job) + { + GNUNET_CURL_job_cancel (tph->job); + tph->job = NULL; + } + TALER_curl_easy_post_finished (&tph->post_ctx); + GNUNET_free (tph->url); + GNUNET_free (tph); +} + + +/* end of merchant_api_post_otp_devices.c */ diff --git a/src/lib/merchant_api_post_templates.c b/src/lib/merchant_api_post_templates.c index bdba52f9..3ab4320c 100644 --- a/src/lib/merchant_api_post_templates.c +++ b/src/lib/merchant_api_post_templates.c @@ -203,8 +203,7 @@ TALER_MERCHANT_templates_post ( const char *backend_url, const char *template_id, const char *template_description, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm, + const char *otp_id, const json_t *template_contract, TALER_MERCHANT_TemplatesPostCallback cb, void *cb_cls) @@ -222,11 +221,9 @@ TALER_MERCHANT_templates_post ( template_id), GNUNET_JSON_pack_string ("template_description", template_description), - GNUNET_JSON_pack_uint64 ("pos_algorithm", - (uint32_t) pos_algorithm), GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("pos_key", - pos_key)), + GNUNET_JSON_pack_string ("otp_id", + otp_id)), GNUNET_JSON_pack_object_incref ("template_contract", (json_t *) template_contract)); tph = GNUNET_new (struct TALER_MERCHANT_TemplatesPostHandle); diff --git a/src/merchant-tools/taler-merchant-benchmark.c b/src/merchant-tools/taler-merchant-benchmark.c index ce4491e0..238b9f03 100644 --- a/src/merchant-tools/taler-merchant-benchmark.c +++ b/src/merchant-tools/taler-merchant-benchmark.c @@ -182,9 +182,13 @@ run (void *cls, "instance-create-default", merchant_url, "default", - cred.user42_payto, MHD_HTTP_NO_CONTENT), - + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-default-account", + merchant_url, + cred.user42_payto, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_admin_add_incoming ( "create-reserve-1", CURRENCY_10_02, @@ -259,14 +263,24 @@ run (void *cls, "instance-create-default", merchant_url, "default", - cred.user42_payto, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-default-account", + merchant_url, + cred.user42_payto, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_merchant_post_instances ( "instance-create-alt", merchant_url, alt_instance_id, - cred.user42_payto, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-alt-account", + alt_instance_url, + cred.user42_payto, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_admin_add_incoming ( "create-reserve-1", CURRENCY_5_01, diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index a2327ad7..d81e6da9 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -37,6 +37,8 @@ libtalermerchanttesting_la_SOURCES = \ testing_api_cmd_get_instance.c \ testing_api_cmd_get_instances.c \ testing_api_cmd_get_orders.c \ + testing_api_cmd_get_otp_device.c \ + testing_api_cmd_get_otp_devices.c \ testing_api_cmd_get_product.c \ testing_api_cmd_get_products.c \ testing_api_cmd_get_reserve.c \ @@ -50,6 +52,7 @@ libtalermerchanttesting_la_SOURCES = \ testing_api_cmd_delete_account.c \ testing_api_cmd_delete_instance.c \ testing_api_cmd_delete_order.c \ + testing_api_cmd_delete_otp_device.c \ testing_api_cmd_delete_product.c \ testing_api_cmd_delete_template.c \ testing_api_cmd_delete_webhook.c \ @@ -61,21 +64,23 @@ libtalermerchanttesting_la_SOURCES = \ testing_api_cmd_instance_auth.c \ testing_api_cmd_merchant_get_order.c \ testing_api_cmd_merchant_get_reward.c \ + testing_api_cmd_patch_instance.c \ + testing_api_cmd_patch_otp_device.c \ + testing_api_cmd_patch_product.c \ + testing_api_cmd_patch_template.c \ + testing_api_cmd_patch_webhook.c \ testing_api_cmd_pay_order.c \ testing_api_cmd_post_account.c \ testing_api_cmd_post_instances.c \ testing_api_cmd_post_orders_paid.c \ testing_api_cmd_post_orders.c \ + testing_api_cmd_post_otp_devices.c \ testing_api_cmd_post_products.c \ testing_api_cmd_post_reserves.c \ testing_api_cmd_post_transfers.c \ testing_api_cmd_post_templates.c \ testing_api_cmd_post_using_templates.c \ testing_api_cmd_post_webhooks.c \ - testing_api_cmd_patch_instance.c \ - testing_api_cmd_patch_product.c \ - testing_api_cmd_patch_template.c \ - testing_api_cmd_patch_webhook.c \ testing_api_cmd_refund_order.c \ testing_api_cmd_reward_authorize.c \ testing_api_cmd_reward_pickup.c \ diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c index e26a1fd3..c8b683d8 100644 --- a/src/testing/test_kyc_api.c +++ b/src/testing/test_kyc_api.c @@ -395,8 +395,13 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_instances ("instance-create-default-setup", merchant_url, "default", - merchant_payto, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-default-account", + merchant_url, + merchant_payto, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_batch ("pay", pay), TALER_TESTING_cmd_batch ("aml", diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c index 32674332..a93ee179 100644 --- a/src/testing/test_merchant_api.c +++ b/src/testing/test_merchant_api.c @@ -120,15 +120,6 @@ static char *merchant_url_i1a; */ #define MERCHANT_ACCOUNT_NAME "3" -/** - * Payto URIs to use for testing accounts on the merchant. - */ -static const char *payto_uris[] = { - PAYTO_I1, - "payto://iban/CH9300762011623852957?receiver-name=Test" - /* Just for testing account inactivation. */ -}; - static const char *order_1_transfers[] = { "post-transfer-1", NULL @@ -223,8 +214,13 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_instances ("instance-create-default", merchant_url, "default", - PAYTO_I1, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-default-account", + merchant_url, + PAYTO_I1, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_merchant_kyc_get ("instance-create-kyc-0", merchant_url, NULL, @@ -939,8 +935,13 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_instances ("instance-create-i1a", merchant_url, "i1a", - PAYTO_I1, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-i1a-account", + merchant_url_i1a, + PAYTO_I1, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_merchant_get_product ("get-nx-product-i1a-1", merchant_url_i1a, "nx-product", @@ -1413,7 +1414,6 @@ run (void *cls, "template-2", "another template", NULL, - TALER_MCA_NONE, GNUNET_JSON_PACK ( GNUNET_JSON_pack_uint64 ("minimum_age", 0), GNUNET_JSON_pack_time_rel ("pay_duration", @@ -1429,13 +1429,21 @@ run (void *cls, "template-nx", MHD_HTTP_NOT_FOUND, NULL), + TALER_TESTING_cmd_merchant_post_otp_devices ( + "post-otp-device", + merchant_url, + "otp-dev", + "my OTP device", + "otp-key", + TALER_MCA_WITH_PRICE, + 0, + MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_patch_template ( "patch-templates-t3-nx", merchant_url, "template-3", "updated template", - NULL, - TALER_MCA_WITH_PRICE, + "otp-dev", GNUNET_JSON_PACK ( GNUNET_JSON_pack_uint64 ("minimum_age", 0), GNUNET_JSON_pack_time_rel ("pay_duration", @@ -1446,8 +1454,7 @@ run (void *cls, merchant_url, "template-amount", "a different template with an amount", - NULL, /* pos_key */ - TALER_MCA_NONE, + NULL, GNUNET_JSON_PACK ( GNUNET_JSON_pack_uint64 ("minimum_age", 0), GNUNET_JSON_pack_time_rel ("pay_duration", @@ -1458,6 +1465,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_using_templates ( "using-templates-t1", "post-templates-t1", + NULL, merchant_url, "1", "summary-1", @@ -1468,6 +1476,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_using_templates ( "using-templates-t1-amount-missing", "post-templates-t1", + NULL, merchant_url, "2", "summary-1", @@ -1478,6 +1487,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_using_templates ( "using-templates-t1-summary-missing", "post-templates-t1", + NULL, merchant_url, "3", NULL, @@ -1488,6 +1498,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_using_templates ( "using-templates-t1-amount-conflict", "post-templates-t3-amount", + NULL, merchant_url, "4", "summary-1", @@ -1498,6 +1509,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_using_templates ( "using-templates-t1-amount-duplicate", "post-templates-t3-amount", + NULL, merchant_url, "4", "summary-1", @@ -1524,6 +1536,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_using_templates ( "post-templates-t1-deleted", "post-templates-t1", + NULL, merchant_url, "0", "summary-1", @@ -1531,13 +1544,21 @@ run (void *cls, GNUNET_TIME_UNIT_ZERO_TS, GNUNET_TIME_UNIT_FOREVER_TS, MHD_HTTP_NOT_FOUND), + TALER_TESTING_cmd_merchant_post_otp_devices ( + "post-otp-device", + merchant_url, + "otp-dev-2", + "my OTP device", + "secret", + TALER_MCA_WITH_PRICE, + 0, + MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_post_templates2 ( "post-templates-with-pos-key", merchant_url, "template-key", "a different template with POS KEY", - "secret", /* pos_key */ - TALER_MCA_WITH_PRICE, + "otp-dev-2", GNUNET_JSON_PACK ( GNUNET_JSON_pack_uint64 ("minimum_age", 0), GNUNET_JSON_pack_time_rel ("pay_duration", @@ -1547,6 +1568,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_using_templates ( "using-templates-pos-key", "post-templates-with-pos-key", + "post-otp-device", merchant_url, "1", "summary-1-pos", @@ -1669,12 +1691,16 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_instances ("instance-create-default-setup", merchant_url, "default", - PAYTO_I1, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-default-account", + merchant_url, + PAYTO_I1, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_merchant_post_instances ("instance-create-i1", merchant_url, "i1", - PAYTO_I1, MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_get_instances ("instances-get-i1", merchant_url, @@ -1690,8 +1716,6 @@ run (void *cls, TALER_TESTING_cmd_merchant_patch_instance ("instance-patch-i1", merchant_url, "i1", - 2, - payto_uris, "bob-the-merchant", json_pack ("{s:s}", "street", @@ -1703,41 +1727,11 @@ run (void *cls, GNUNET_TIME_UNIT_MINUTES, GNUNET_TIME_UNIT_MINUTES, MHD_HTTP_NO_CONTENT), - TALER_TESTING_cmd_merchant_get_instance2 ("instances-get-i1-2", - merchant_url, - "i1", - MHD_HTTP_OK, - "instance-patch-i1", - payto_uris, - 2, - NULL, - 0), - TALER_TESTING_cmd_merchant_patch_instance ( - "instance-patch-i1-inactivate-account", - merchant_url, - "i1", - 1, - payto_uris, - "bob-the-merchant", - json_pack ("{s:s}", - "street", - "bobstreet"), - json_pack ("{s:s}", - "street", - "bobjuryst"), - true, - GNUNET_TIME_UNIT_MINUTES, - GNUNET_TIME_UNIT_MINUTES, - MHD_HTTP_NO_CONTENT), - TALER_TESTING_cmd_merchant_get_instance2 ("instances-get-i1-3", - merchant_url, - "i1", - MHD_HTTP_OK, - "instance-patch-i1-inactivate-account", - payto_uris, - 1, - &payto_uris[1], - 1), + TALER_TESTING_cmd_merchant_get_instance ("instances-get-i1-2", + merchant_url, + "i1", + MHD_HTTP_OK, + "instance-patch-i1"), TALER_TESTING_cmd_merchant_get_instance ("instances-get-i2-nx", merchant_url, "i2", @@ -1746,7 +1740,6 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_instances2 ("instance-create-ACL", merchant_url, "i-acl", - 0, NULL, "controlled instance", json_pack ("{s:s}", "city", "shopcity"), @@ -1763,8 +1756,6 @@ run (void *cls, TALER_TESTING_cmd_merchant_patch_instance ("instance-patch-ACL", merchant_url, "i-acl", - 1, - payto_uris, "controlled instance", json_pack ("{s:s}", "street", @@ -1779,18 +1770,11 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2", merchant_url, "i2", - PAYTO_I1, MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2-idem", merchant_url, "i2", - PAYTO_I1, MHD_HTTP_NO_CONTENT), - TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2-non-idem", - merchant_url, - "i2", - "payto://other-method/?receiver-name=X", - MHD_HTTP_CONFLICT), TALER_TESTING_cmd_merchant_delete_instance ("instance-delete-i2", merchant_url, "i2", @@ -1824,8 +1808,13 @@ run (void *cls, "instance-create-default-after-purge", merchant_url, "default", - PAYTO_I1, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-default-account-after-purge", + merchant_url, + PAYTO_I1, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_merchant_get_products ("get-products-empty", merchant_url, MHD_HTTP_OK, diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1692810704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1692810704 deleted file mode 100644 index 7fd4a991..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1692810704 +++ /dev/null @@ -1 +0,0 @@ -¼KÕ¯€H"r^Y³gÌn€Šˆ2²^ðЋÉ
\ No newline at end of file diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1692810704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1692810704 deleted file mode 100644 index 609223fc..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1692810704 +++ /dev/null @@ -1,2 +0,0 @@ -Âò€ äç6 -–‚M¦îð‡…ê€üRñO&4žÿg¢
\ No newline at end of file diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1692810704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1692810704 deleted file mode 100644 index a03556d9..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1692810704 +++ /dev/null @@ -1 +0,0 @@ -0›b„‘úmÍqjÑËfÓŒÈêáöYP¥Z¨Ñø
\ No newline at end of file diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1692810704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1692810704 deleted file mode 100644 index d43290a6..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1692810704 +++ /dev/null @@ -1 +0,0 @@ -ˆÌÿµyÌ\ö‘UúçB”Î6Òš±6Dê„„Ìé&
\ No newline at end of file diff --git a/src/testing/test_merchant_api_twisted.c b/src/testing/test_merchant_api_twisted.c index b55388ab..71a6485b 100644 --- a/src/testing/test_merchant_api_twisted.c +++ b/src/testing/test_merchant_api_twisted.c @@ -337,8 +337,13 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_instances ("instance-create-default", twister_merchant_url, "default", - PAYTO_I1, MHD_HTTP_NO_CONTENT), + TALER_TESTING_cmd_merchant_post_account ( + "instance-create-default-account", + twister_merchant_url, + PAYTO_I1, + NULL, NULL, + MHD_HTTP_OK), TALER_TESTING_cmd_batch ("pay", pay), /* Malform the response from the exchange. */ diff --git a/src/testing/test_merchant_instance_auth.sh b/src/testing/test_merchant_instance_auth.sh index 5cc4de92..8d6e347a 100755 --- a/src/testing/test_merchant_instance_auth.sh +++ b/src/testing/test_merchant_instance_auth.sh @@ -42,13 +42,26 @@ echo -n "Configuring 'default' instance ..." >&2 STATUS=$(curl -H "Content-Type: application/json" -X POST \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"token","token":"secret-token:new_value"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"token","token":"secret-token:new_value"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] then exit_fail "Expected 204, instance created. got: $STATUS" >&2 fi + +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:new_value' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"payto://x-taler-bank/localhost:8082/43"}' \ + -w "%{http_code}" -s -o /dev/null) + + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi + echo " OK" >&2 # Kill merchant @@ -115,7 +128,7 @@ echo -n "Configuring 'second' instance ..." >&2 STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer '$NEW_SECRET \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"token","token":"secret-token:second"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"second","name":"second","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"token","token":"secret-token:second"},"id":"second","name":"second","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] diff --git a/src/testing/test_merchant_instance_creation.sh b/src/testing/test_merchant_instance_creation.sh index 8d81ab8b..c7eda54b 100755 --- a/src/testing/test_merchant_instance_creation.sh +++ b/src/testing/test_merchant_instance_creation.sh @@ -27,7 +27,7 @@ echo -n "Configuring a merchant instance before configuring the default instance STATUS=$(curl -H "Content-Type: application/json" -X POST \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"first","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"first","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] @@ -43,7 +43,7 @@ echo -n "Configuring default instance ..." STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "401" ] @@ -58,7 +58,7 @@ echo -n "Configuring a second merchant instance ..." STATUS=$(curl -H "Content-Type: application/json" -X POST \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"second","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"second","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "401" ] diff --git a/src/testing/test_merchant_instance_purge.sh b/src/testing/test_merchant_instance_purge.sh index 0d8c0c2c..f3992495 100755 --- a/src/testing/test_merchant_instance_purge.sh +++ b/src/testing/test_merchant_instance_purge.sh @@ -27,7 +27,7 @@ echo -n "Configuring default instance ..." >&2 STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] @@ -41,7 +41,7 @@ echo -n "Configuring merchant instance ..." >&2 STATUS=$(curl -H "Content-Type: application/json" -X POST \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"test","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"test","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] diff --git a/src/testing/test_merchant_instance_response.sh b/src/testing/test_merchant_instance_response.sh index b2eee199..336c33cc 100755 --- a/src/testing/test_merchant_instance_response.sh +++ b/src/testing/test_merchant_instance_response.sh @@ -29,7 +29,7 @@ STATUS=$(curl -H "Content-Type: application/json" -X OPTIONS \ if [ "$STATUS" != "204" ] then - exit_fail "Expected 204 when default instance doest not exist yet. got: $STATUS" + exit_fail "Expected 204 when default instance does not exist yet. got: $STATUS" fi STATUS=$(curl -H "Content-Type: application/json" -X GET \ @@ -45,7 +45,7 @@ fi STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ + -d '{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] diff --git a/src/testing/test_merchant_kyc.sh b/src/testing/test_merchant_kyc.sh index 2b3bb188..663590b1 100755 --- a/src/testing/test_merchant_kyc.sh +++ b/src/testing/test_merchant_kyc.sh @@ -24,12 +24,12 @@ set -eu setup -c "test_template.conf" -m -u "exchange-account-1" LAST_RESPONSE=$(mktemp -p "${TMPDIR:-/tmp}" test_response.conf-XXXXXX) -echo -n "Configuring a merchant instance before configuring the default instance ..." +echo -n "Configuring a merchant default instance ..." STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost:8082/43"},{"payto_uri":"payto://x-taler-bank/localhost:8082/44"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] @@ -37,6 +37,30 @@ then exit_fail "Expected 204 ok, instance created. got: $STATUS" fi +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"payto://x-taler-bank/localhost:8082/43"}' \ + -w "%{http_code}" -s -o /dev/null) + + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi + +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"payto://x-taler-bank/localhost:8082/44"}' \ + -w "%{http_code}" -s -o /dev/null) + + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi + echo " OK" echo -n "Check the instance exists ..." diff --git a/src/testing/test_merchant_order_autocleanup.sh b/src/testing/test_merchant_order_autocleanup.sh index 08655d18..60ee19e0 100755 --- a/src/testing/test_merchant_order_autocleanup.sh +++ b/src/testing/test_merchant_order_autocleanup.sh @@ -83,11 +83,10 @@ else FORTYTHREE=$(get_payto_uri fortythree x) fi -# create with 2 address STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ "http://localhost:9966/management/instances" \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] @@ -95,6 +94,17 @@ then exit_fail "Expected 204, instance created. got: $STATUS" fi +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"'"$FORTYTHREE"'"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected '200 OK' response. Got instead $STATUS" +fi + NOW=$(date +%s) IN_TEN_SECS=$(( NOW + 10 )) diff --git a/src/testing/test_merchant_order_creation.sh b/src/testing/test_merchant_order_creation.sh index 1461c005..f0355565 100755 --- a/src/testing/test_merchant_order_creation.sh +++ b/src/testing/test_merchant_order_creation.sh @@ -83,6 +83,20 @@ fi echo -n "Configuring merchant instance ..." +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/management/instances \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000000},"default_pay_delay":{"d_us": 60000000000}}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "204" ] +then + exit_fail "Expected '204 No content' response. Got instead $STATUS" +fi +echo "Ok" + +echo -n "Configuring merchant account ..." + if [ 1 = "$USE_FAKEBANK" ] then FORTYTHREE="payto://x-taler-bank/localhost/fortythree?receiver-name=fortythree" @@ -92,26 +106,51 @@ fi # create with 2 bank account addresses STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ - http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"},{"payto_uri":"payto://iban/SANDBOXX/DE270744?receiver-name=Forty+Four"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000000},"default_pay_delay":{"d_us": 60000000000}}' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"'"$FORTYTHREE"'"}' \ -w "%{http_code}" -s -o /dev/null) -if [ "$STATUS" != "204" ] +if [ "$STATUS" != "200" ] then - exit_fail "Expected '204 No content' response. Got instead $STATUS" + exit_fail "Expected '200 OK' response. Got instead $STATUS" fi -echo -n "." -# remove one account address +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"payto://iban/SANDBOXX/DE270744?receiver-name=Forty+Four"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected '200 OK' response. Got instead $STATUS" +fi + +echo "Ok" + +echo -n "Get accounts..." +STATUS=$(curl http://localhost:9966/instances/default/private/accounts \ + -w "%{http_code}" -s -o "$LAST_RESPONSE") +PAY_URI=$(jq -r .accounts[1].payto_uri < "$LAST_RESPONSE") +H_WIRE=$(jq -r .accounts[1].h_wire < "$LAST_RESPONSE") +if [ "$PAY_URI" != "payto://iban/SANDBOXX/DE270744?receiver-name=Forty+Four" ] +then + cat "$LAST_RESPONSE" >&2 + exit_fail "Expected second payto URI. Got $PAY_URI" +fi +echo "OK" + +# remove one account address +echo -n "Deleting one account ..." STATUS=$(curl -H "Content-Type: application/json" -X PATCH \ -H 'Authorization: Bearer secret-token:super_secret' \ - http://localhost:9966/instances/default/private/ \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000000},"default_pay_delay":{"d_us": 60000000000}}' \ + "http://localhost:9966/instances/default/private/accounts/${H_WIRE}" \ + -X DELETE \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] then - exit_fail "Expected '204 No content' response. Got instead: $STATUS" + exit_fail "Expected '204 No content' for deletion of ${H_WIRE}. Got instead: $STATUS" fi echo "OK" diff --git a/src/testing/test_merchant_product_creation.sh b/src/testing/test_merchant_product_creation.sh index dd3a60b5..6da5efa2 100755 --- a/src/testing/test_merchant_product_creation.sh +++ b/src/testing/test_merchant_product_creation.sh @@ -55,13 +55,25 @@ echo -n "Configuring merchant instance ..." STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ "http://localhost:9966/management/instances" \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] then exit_fail "Expected 204 No content, instance created. Got $STATUS instead" fi + +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"'"$FORTYTHREE"'"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi + echo "OK" RANDOM_IMG='data:image/png;base64,abcdefg' diff --git a/src/testing/test_merchant_reserve_creation.sh b/src/testing/test_merchant_reserve_creation.sh index e7ebf615..36e71e99 100755 --- a/src/testing/test_merchant_reserve_creation.sh +++ b/src/testing/test_merchant_reserve_creation.sh @@ -47,17 +47,29 @@ echo -n "Configuring merchant instance ..." STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost:18082/fortythree"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] then exit_fail "Expected 204, instance created. Got instead: $STATUS" fi +echo "Ok" +echo -n "Configuring merchant account ..." +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"payto://x-taler-bank/localhost:18082/fortythree"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi + echo "OK" echo -n "Creating reserve ..." -#bash STATUS=$(curl 'http://localhost:9966/instances/default/private/reserves' \ -d '{"initial_balance":"TESTKUDOS:2","exchange_url":"http://localhost:8081/","wire_method":"'"$WIRE_METHOD"'"}' \ diff --git a/src/testing/test_merchant_transfer_tracking.sh b/src/testing/test_merchant_transfer_tracking.sh index cba85d12..3a2a18a9 100755 --- a/src/testing/test_merchant_transfer_tracking.sh +++ b/src/testing/test_merchant_transfer_tracking.sh @@ -108,13 +108,35 @@ fi STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$TOR_PAYTO"'"},{"payto_uri":"'"$GNUNET_PAYTO"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] then exit_fail "Expected 204, instance created. got: $STATUS" fi + +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"'"$TOR_PAYTO"'"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"'"$GNUNET_PAYTO"'"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi + echo "OK" echo -n "Configuring merchant test instance ..." @@ -123,13 +145,33 @@ echo -n "Configuring merchant test instance ..." STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$SURVEY_PAYTO"'"},{"payto_uri":"'"$TUTORIAL_PAYTO"'"}],"id":"test","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ + -d '{"auth":{"method":"external"},"id":"test","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] then exit_fail "Expected 204, instance created. got: $STATUS" fi +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/test/private/accounts \ + -d '{"payto_uri":"'"$SURVEY_PAYTO"'"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/test/private/accounts \ + -d '{"payto_uri":"'"$TUTORIAL_PAYTO"'"}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi echo "OK" # CREATE ORDER AND SELL IT diff --git a/src/testing/test_merchant_wirewatch.sh b/src/testing/test_merchant_wirewatch.sh index b8a37f2f..b4fdc66b 100755 --- a/src/testing/test_merchant_wirewatch.sh +++ b/src/testing/test_merchant_wirewatch.sh @@ -197,13 +197,26 @@ fi STATUS=$(curl -H "Content-Type: application/json" -X POST \ -H 'Authorization: Bearer secret-token:super_secret' \ http://localhost:9966/management/instances \ - -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$GNUNET_PAYTO"'","credit_facade_url":"'"${FACADE_URL}"'","credit_facade_credentials":{"type":"basic","username":"'"$FACADE_USERNAME"'","password":"'"$FACADE_PASSWORD"'"}}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ + -d '{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us" : 50000000},"default_pay_delay":{"d_us": 60000000}}' \ -w "%{http_code}" -s -o /dev/null) if [ "$STATUS" != "204" ] then exit_fail "Expected 204 no content. Got: $STATUS" fi +echo "OK" + +echo -n "Configuring bank account..." +STATUS=$(curl -H "Content-Type: application/json" -X POST \ + -H 'Authorization: Bearer secret-token:super_secret' \ + http://localhost:9966/instances/default/private/accounts \ + -d '{"payto_uri":"'"$GNUNET_PAYTO"'","credit_facade_url":"'"${FACADE_URL}"'","credit_facade_credentials":{"type":"basic","username":"'"$FACADE_USERNAME"'","password":"'"$FACADE_PASSWORD"'"}}' \ + -w "%{http_code}" -s -o /dev/null) + +if [ "$STATUS" != "200" ] +then + exit_fail "Expected 200 OK. Got: $STATUS" +fi echo "OK" diff --git a/src/testing/testing_api_cmd_delete_account.c b/src/testing/testing_api_cmd_delete_account.c index 1490dc31..681faa3c 100644 --- a/src/testing/testing_api_cmd_delete_account.c +++ b/src/testing/testing_api_cmd_delete_account.c @@ -29,7 +29,7 @@ /** - * State of a "DELETE /accounts/$ID" CMD. + * State of a "DELETE /accounts/$H_WIRE" CMD. */ struct DeleteAccountState { @@ -52,12 +52,7 @@ struct DeleteAccountState /** * ID of the command to get account details from. */ - const char *get_instance_ref; - - /** - * Payto URI to extract h_wire from. - */ - const char *payto_uri; + const char *create_account_ref; /** * Expected HTTP response code. @@ -128,7 +123,7 @@ delete_account_run (void *cls, das->is = is; ref = TALER_TESTING_interpreter_lookup_command (is, - das->get_instance_ref); + das->create_account_ref); if (NULL == ref) { GNUNET_break (0); @@ -141,45 +136,22 @@ delete_account_run (void *cls, { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Command %s lacked merchant base URL\n", - das->get_instance_ref); + das->create_account_ref); GNUNET_break (0); TALER_TESTING_FAIL (is); return; } - for (unsigned int i = 0; i<UINT_MAX; i++) + if (GNUNET_OK != + TALER_TESTING_get_trait_h_wires (ref, + 0, + &h_wire)) { - const char *payto_uri; - - if (GNUNET_OK != - TALER_TESTING_get_trait_payto_uris (ref, - i, - &payto_uri)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Command %s did not return payto URI %s\n", - das->get_instance_ref, - das->payto_uri); - GNUNET_break (0); - TALER_TESTING_FAIL (is); - return; - } - if (0 != strcmp (payto_uri, - das->payto_uri)) - continue; /* different account */ - if (GNUNET_OK != - TALER_TESTING_get_trait_h_wires (ref, - i, - &h_wire)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Command %s had payto URI %s but lacked h_wire!?\n", - das->get_instance_ref, - das->payto_uri); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command %s did not return H_WIRE\n", + das->create_account_ref); GNUNET_break (0); TALER_TESTING_FAIL (is); return; - } - break; } GNUNET_assert (NULL != h_wire); das->adh = TALER_MERCHANT_account_delete ( @@ -217,15 +189,13 @@ delete_account_cleanup (void *cls, struct TALER_TESTING_Command TALER_TESTING_cmd_merchant_delete_account (const char *label, - const char *get_instance_ref, - const char *payto_uri, + const char *create_account_ref, unsigned int http_status) { struct DeleteAccountState *das; das = GNUNET_new (struct DeleteAccountState); - das->get_instance_ref = get_instance_ref; - das->payto_uri = payto_uri; + das->create_account_ref = create_account_ref; das->http_status = http_status; { struct TALER_TESTING_Command cmd = { diff --git a/src/testing/testing_api_cmd_delete_otp_device.c b/src/testing/testing_api_cmd_delete_otp_device.c new file mode 100644 index 00000000..3d15c645 --- /dev/null +++ b/src/testing/testing_api_cmd_delete_otp_device.c @@ -0,0 +1,181 @@ +/* + This file is part of TALER + 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 Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing_api_cmd_delete_otp_device.c + * @brief command to test DELETE /otp-devices/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "DELETE /otp-devices/$ID" CMD. + */ +struct DeleteOtpDeviceState +{ + + /** + * Handle for a "DELETE otp_device" request. + */ + struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the otp_device to run DELETE for. + */ + const char *otp_device_id; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a /delete/otp-devices/$ID operation. + * + * @param cls closure for this function + * @param hr response being processed + */ +static void +delete_otp_device_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr) +{ + struct DeleteOtpDeviceState *dis = cls; + + dis->tdh = NULL; + if (dis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (dis->is)); + TALER_TESTING_interpreter_fail (dis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + break; + case MHD_HTTP_NOT_FOUND: + break; + case MHD_HTTP_CONFLICT: + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status %u for DELETE otp_device.\n", + hr->http_status); + } + TALER_TESTING_interpreter_next (dis->is); +} + + +/** + * Run the "DELETE otp_device" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +delete_otp_device_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct DeleteOtpDeviceState *dis = cls; + + dis->is = is; + dis->tdh = TALER_MERCHANT_otp_device_delete ( + TALER_TESTING_interpreter_get_context (is), + dis->merchant_url, + dis->otp_device_id, + &delete_otp_device_cb, + dis); + GNUNET_assert (NULL != dis->tdh); +} + + +/** + * Free the state of a "DELETE otp_device" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +delete_otp_device_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct DeleteOtpDeviceState *dis = cls; + + if (NULL != dis->tdh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "DELETE /otp-devices/$ID operation did not complete\n"); + TALER_MERCHANT_otp_device_delete_cancel (dis->tdh); + } + GNUNET_free (dis); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_delete_otp_device (const char *label, + const char *merchant_url, + const char *otp_device_id, + unsigned int http_status) +{ + struct DeleteOtpDeviceState *dis; + + dis = GNUNET_new (struct DeleteOtpDeviceState); + dis->merchant_url = merchant_url; + dis->otp_device_id = otp_device_id; + dis->http_status = http_status; + { + struct TALER_TESTING_Command cmd = { + .cls = dis, + .label = label, + .run = &delete_otp_device_run, + .cleanup = &delete_otp_device_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_delete_otp_device.c */ diff --git a/src/testing/testing_api_cmd_get_instance.c b/src/testing/testing_api_cmd_get_instance.c index e706f85b..c3199a7e 100644 --- a/src/testing/testing_api_cmd_get_instance.c +++ b/src/testing/testing_api_cmd_get_instance.c @@ -29,39 +29,6 @@ /** - * Details about a merchant's bank account. - */ -struct MERCHANT_Account -{ - /** - * salt used to compute h_wire - */ - struct TALER_WireSaltP salt; - - /** - * payto:// URI of the account. - */ - char *payto_uri; - - /** - * Credit facade URL of the account. - */ - char *credit_facade_url; - - /** - * Hash of @e payto_uri and @e salt. - */ - struct TALER_MerchantWireHashP h_wire; - - /** - * true if the account is active, - * false if it is historic. - */ - bool active; -}; - - -/** * State of a "GET instance" CMD. */ struct GetInstanceState @@ -93,42 +60,6 @@ struct GetInstanceState const char *instance_reference; /** - * Whether we should check the instance's accounts or not. - */ - bool cmp_accounts; - - /** - * The accounts of the merchant we expect to be active. - */ - const char **active_accounts; - - /** - * The length of @e active_accounts. - */ - unsigned int active_accounts_length; - - /** - * The accounts of the merchant we expect to be inactive. - */ - const char **inactive_accounts; - - /** - * Length of the @e accounts array. - */ - unsigned int accounts_length; - - /** - * Array of @e accounts_length bank accounts of the merchant instance as - * returned by the request. - */ - struct MERCHANT_Account *accounts; - - /** - * The length of @e inactive_accounts. - */ - unsigned int inactive_accounts_length; - - /** * Expected HTTP response code. */ unsigned int http_status; @@ -171,21 +102,6 @@ get_instance_cb (void *cls, const struct TALER_MERCHANT_InstanceDetails *details = &igr->details.ok.details; - gis->accounts_length = igr->details.ok.accounts_length; - gis->accounts = GNUNET_new_array (gis->accounts_length, - struct MERCHANT_Account); - for (unsigned int i = 0; i<gis->accounts_length; i++) - { - const struct TALER_MERCHANT_Account *src = &igr->details.ok.accounts[i]; - struct MERCHANT_Account *dst = &gis->accounts[i]; - - dst->salt = src->salt; - dst->payto_uri = GNUNET_strdup (src->payto_uri); - if (NULL != src->credit_facade_url) - dst->credit_facade_url = GNUNET_strdup (src->credit_facade_url); - dst->h_wire = src->h_wire; - dst->active = src->active; - } { const char *name; @@ -283,65 +199,6 @@ get_instance_cb (void *cls, return; } } - /* We aren't guaranteed an order for the accounts, so we just have to check - that we can match each account returned with exactly one account - expected. */ - if (gis->cmp_accounts) - { - unsigned int have_al = igr->details.ok.accounts_length; - unsigned int expected_accounts_length = - gis->active_accounts_length + gis->inactive_accounts_length; - unsigned int matches[GNUNET_NZL (have_al)]; - - if (have_al != expected_accounts_length) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Accounts length does not match\n"); - TALER_TESTING_interpreter_fail (gis->is); - return; - } - - memset (matches, - 0, - sizeof (matches)); - - /* Compare the accounts */ - for (unsigned int i = 0; i < have_al; ++i) - { - const struct TALER_MERCHANT_Account *account - = &igr->details.ok.accounts[i]; - for (unsigned int j = 0; j < gis->active_accounts_length; ++j) - { - if ((0 == strcasecmp (account->payto_uri, - gis->active_accounts[j])) && - account->active) - { - matches[i] += 1; - } - } - for (unsigned int j = 0; j < gis->inactive_accounts_length; ++j) - { - if ((0 == strcasecmp (account->payto_uri, - gis->inactive_accounts[j])) && - (! account->active)) - { - matches[i] += 1; - } - } - } - - // Each account should have exactly one match. - for (unsigned int i = 0; i < have_al; ++i) - { - if (1 != matches[i]) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Instance account does not match\n"); - TALER_TESTING_interpreter_fail (gis->is); - return; - } - } - } } break; case MHD_HTTP_UNAUTHORIZED: @@ -402,14 +259,6 @@ get_instance_cleanup (void *cls, "GET /instances/$ID operation did not complete\n"); TALER_MERCHANT_instance_get_cancel (gis->igh); } - for (unsigned int i = 0; i<gis->accounts_length; i++) - { - struct MERCHANT_Account *acc = &gis->accounts[i]; - - GNUNET_free (acc->payto_uri); - GNUNET_free (acc->credit_facade_url); - } - GNUNET_free (gis->accounts); GNUNET_free (gis); } @@ -431,37 +280,15 @@ get_instance_traits (void *cls, unsigned int index) { struct GetInstanceState *pps = cls; - - if (index < pps->accounts_length) - { - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_merchant_base_url (pps->merchant_url), - TALER_TESTING_make_trait_h_wires ( - index, - &pps->accounts[index].h_wire), - TALER_TESTING_make_trait_payto_uris ( - index, - pps->accounts[index].payto_uri), - TALER_TESTING_trait_end (), - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); - } - else - { - struct TALER_TESTING_Trait traits[] = { - TALER_TESTING_make_trait_merchant_base_url (pps->merchant_url), - TALER_TESTING_trait_end (), - }; - - return TALER_TESTING_get_trait (traits, - ret, - trait, - index); - } + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_merchant_base_url (pps->merchant_url), + TALER_TESTING_trait_end (), + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); } @@ -479,44 +306,6 @@ TALER_TESTING_cmd_merchant_get_instance (const char *label, gis->instance_id = instance_id; gis->http_status = http_status; gis->instance_reference = instance_reference; - gis->cmp_accounts = false; - { - struct TALER_TESTING_Command cmd = { - .cls = gis, - .label = label, - .run = &get_instance_run, - .cleanup = &get_instance_cleanup, - .traits = &get_instance_traits - }; - - return cmd; - } -} - - -struct TALER_TESTING_Command -TALER_TESTING_cmd_merchant_get_instance2 (const char *label, - const char *merchant_url, - const char *instance_id, - unsigned int http_status, - const char *instance_reference, - const char *active_accounts[], - unsigned int active_accounts_length, - const char *inactive_accounts[], - unsigned int inactive_accounts_length) -{ - struct GetInstanceState *gis; - - gis = GNUNET_new (struct GetInstanceState); - gis->merchant_url = merchant_url; - gis->instance_id = instance_id; - gis->http_status = http_status; - gis->instance_reference = instance_reference; - gis->cmp_accounts = true; - gis->active_accounts = active_accounts; - gis->active_accounts_length = active_accounts_length; - gis->inactive_accounts = inactive_accounts; - gis->inactive_accounts_length = inactive_accounts_length; { struct TALER_TESTING_Command cmd = { .cls = gis, diff --git a/src/testing/testing_api_cmd_get_otp_device.c b/src/testing/testing_api_cmd_get_otp_device.c new file mode 100644 index 00000000..272039af --- /dev/null +++ b/src/testing/testing_api_cmd_get_otp_device.c @@ -0,0 +1,227 @@ +/* + This file is part of TALER + 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 Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing_api_cmd_get_otp_device.c + * @brief command to test GET /otp-devices/$ID + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "GET OTP device" CMD. + */ +struct GetOtpDeviceState +{ + + /** + * Handle for a "GET /otp-device/$ID" request. + */ + struct TALER_MERCHANT_OtpDeviceGetHandle *igh; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the otp_device to run GET for. + */ + const char *otp_device_id; + + /** + * Reference for a POST or PATCH /otp-devices CMD (optional). + */ + const char *otp_device_reference; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a GET /otp-devices/$ID operation. + * + * @param cls closure for this function + * @param tgr HTTP response details + */ +static void +get_otp_device_cb (void *cls, + const struct TALER_MERCHANT_OtpDeviceGetResponse *tgr) +{ + struct GetOtpDeviceState *gis = cls; + const struct TALER_TESTING_Command *otp_device_cmd; + + gis->igh = NULL; + if (gis->http_status != tgr->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + tgr->hr.http_status, + (int) tgr->hr.ec, + TALER_TESTING_interpreter_get_current_label (gis->is)); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + switch (tgr->hr.http_status) + { + case MHD_HTTP_OK: + { + const char *expected_description; + + otp_device_cmd = TALER_TESTING_interpreter_lookup_command ( + gis->is, + gis->otp_device_reference); + if (GNUNET_OK != + TALER_TESTING_get_trait_otp_device_description (otp_device_cmd, + &expected_description)) + TALER_TESTING_interpreter_fail (gis->is); + if (0 != strcmp (tgr->details.ok.otp_device_description, + expected_description)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "OtpDevice description does not match\n"); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + } + { + const char *expected_otp_key; + + if (GNUNET_OK != + TALER_TESTING_get_trait_otp_key (otp_device_cmd, + &expected_otp_key)) + TALER_TESTING_interpreter_fail (gis->is); + if ( ( (NULL == tgr->details.ok.otp_key) && (NULL != expected_otp_key)) || + ( (NULL != tgr->details.ok.otp_key) && (NULL == expected_otp_key)) || + ( (NULL != tgr->details.ok.otp_key) && + (0 != strcmp (tgr->details.ok.otp_key, + expected_otp_key)) ) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "OtpDevice otp_key `%s' does not match `%s'\n", + tgr->details.ok.otp_key, + expected_otp_key); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + } + break; + case MHD_HTTP_UNAUTHORIZED: + break; + case MHD_HTTP_NOT_FOUND: + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status.\n"); + } + TALER_TESTING_interpreter_next (gis->is); +} + + +/** + * Run the "GET /otp-device/$ID" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +get_otp_device_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct GetOtpDeviceState *gis = cls; + + gis->is = is; + gis->igh = TALER_MERCHANT_otp_device_get ( + TALER_TESTING_interpreter_get_context (is), + gis->merchant_url, + gis->otp_device_id, + &get_otp_device_cb, + gis); + GNUNET_assert (NULL != gis->igh); +} + + +/** + * Free the state of a "GET /otp-device/$ID" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +get_otp_device_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct GetOtpDeviceState *gis = cls; + + if (NULL != gis->igh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "GET /otp-devices/$ID operation did not complete\n"); + TALER_MERCHANT_otp_device_get_cancel (gis->igh); + } + GNUNET_free (gis); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_otp_device ( + const char *label, + const char *merchant_url, + const char *otp_device_id, + unsigned int http_status, + const char *otp_device_reference) +{ + struct GetOtpDeviceState *gis; + + gis = GNUNET_new (struct GetOtpDeviceState); + gis->merchant_url = merchant_url; + gis->otp_device_id = otp_device_id; + gis->http_status = http_status; + gis->otp_device_reference = otp_device_reference; + { + struct TALER_TESTING_Command cmd = { + .cls = gis, + .label = label, + .run = &get_otp_device_run, + .cleanup = &get_otp_device_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_get_otp_device.c */ diff --git a/src/testing/testing_api_cmd_get_otp_devices.c b/src/testing/testing_api_cmd_get_otp_devices.c new file mode 100644 index 00000000..b4f370c5 --- /dev/null +++ b/src/testing/testing_api_cmd_get_otp_devices.c @@ -0,0 +1,238 @@ +/* + This file is part of TALER + 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 Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing_api_cmd_get_otp_devices.c + * @brief command to test GET /otp-devices + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "GET /otp-devices" CMD. + */ +struct GetOtpDevicesState +{ + + /** + * Handle for a "GET /otp-devices" request. + */ + struct TALER_MERCHANT_OtpDevicesGetHandle *igh; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + + /** + * The list of otp_device references. + */ + const char **otp_devices; + + /** + * Length of @e otp_devices. + */ + unsigned int otp_devices_length; + +}; + + +/** + * Callback for a GET /otp-devices operation. + * + * @param cls closure for this function + * @param tgr response details + */ +static void +get_otp_devices_cb (void *cls, + const struct TALER_MERCHANT_OtpDevicesGetResponse *tgr) +{ + struct GetOtpDevicesState *gis = cls; + + gis->igh = NULL; + if (gis->http_status != tgr->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + tgr->hr.http_status, + (int) tgr->hr.ec, + TALER_TESTING_interpreter_get_current_label (gis->is)); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + switch (tgr->hr.http_status) + { + case MHD_HTTP_OK: + if (tgr->details.ok.otp_devices_length != gis->otp_devices_length) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Length of otp_devices found does not match\n"); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + for (unsigned int i = 0; i < gis->otp_devices_length; ++i) + { + const struct TALER_TESTING_Command *otp_device_cmd; + + otp_device_cmd = TALER_TESTING_interpreter_lookup_command ( + gis->is, + gis->otp_devices[i]); + + { + const char *otp_device_id; + + if (GNUNET_OK != + TALER_TESTING_get_trait_otp_id (otp_device_cmd, + &otp_device_id)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not fetch otp_device id\n"); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + if (0 != strcmp (tgr->details.ok.otp_devices[i].otp_device_id, + otp_device_id)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "OtpDevice id does not match\n"); + TALER_TESTING_interpreter_fail (gis->is); + return; + } + } + } + break; + case MHD_HTTP_UNAUTHORIZED: + break; + case MHD_HTTP_NOT_FOUND: + /* instance does not exist */ + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status %u (%d).\n", + tgr->hr.http_status, + tgr->hr.ec); + break; + } + TALER_TESTING_interpreter_next (gis->is); +} + + +/** + * Run the "GET /otp-devices" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +get_otp_devices_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct GetOtpDevicesState *gis = cls; + + gis->is = is; + gis->igh = TALER_MERCHANT_otp_devices_get ( + TALER_TESTING_interpreter_get_context (is), + gis->merchant_url, + &get_otp_devices_cb, + gis); + GNUNET_assert (NULL != gis->igh); +} + + +/** + * Free the state of a "GET otp_device" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +get_otp_devices_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct GetOtpDevicesState *gis = cls; + + if (NULL != gis->igh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "GET /otp-devices operation did not complete\n"); + TALER_MERCHANT_otp_devices_get_cancel (gis->igh); + } + GNUNET_array_grow (gis->otp_devices, + gis->otp_devices_length, + 0); + GNUNET_free (gis); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_get_otp_devices (const char *label, + const char *merchant_url, + unsigned int http_status, + ...) +{ + struct GetOtpDevicesState *gis; + + gis = GNUNET_new (struct GetOtpDevicesState); + gis->merchant_url = merchant_url; + gis->http_status = http_status; + { + const char *clabel; + va_list ap; + + va_start (ap, http_status); + while (NULL != (clabel = va_arg (ap, const char *))) + { + GNUNET_array_append (gis->otp_devices, + gis->otp_devices_length, + clabel); + } + va_end (ap); + } + { + struct TALER_TESTING_Command cmd = { + .cls = gis, + .label = label, + .run = &get_otp_devices_run, + .cleanup = &get_otp_devices_cleanup + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_get_otp_devices.c */ diff --git a/src/testing/testing_api_cmd_get_template.c b/src/testing/testing_api_cmd_get_template.c index 92888e6f..377ffe44 100644 --- a/src/testing/testing_api_cmd_get_template.c +++ b/src/testing/testing_api_cmd_get_template.c @@ -114,22 +114,22 @@ get_template_cb (void *cls, } } { - const char *expected_pos_key; + const char *expected_otp_id; if (GNUNET_OK != - TALER_TESTING_get_trait_template_pos_key (template_cmd, - &expected_pos_key)) + TALER_TESTING_get_trait_otp_id (template_cmd, + &expected_otp_id)) TALER_TESTING_interpreter_fail (gis->is); - if ( ( (NULL == tgr->details.ok.pos_key) && (NULL != expected_pos_key)) || - ( (NULL != tgr->details.ok.pos_key) && (NULL == expected_pos_key)) || - ( (NULL != tgr->details.ok.pos_key) && - (0 != strcmp (tgr->details.ok.pos_key, - expected_pos_key)) ) ) + if ( ( (NULL == tgr->details.ok.otp_id) && (NULL != expected_otp_id)) || + ( (NULL != tgr->details.ok.otp_id) && (NULL == expected_otp_id)) || + ( (NULL != tgr->details.ok.otp_id) && + (0 != strcmp (tgr->details.ok.otp_id, + expected_otp_id)) ) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Template pos_key `%s' does not match `%s'\n", - tgr->details.ok.pos_key, - expected_pos_key); + tgr->details.ok.otp_id, + expected_otp_id); TALER_TESTING_interpreter_fail (gis->is); return; } diff --git a/src/testing/testing_api_cmd_patch_instance.c b/src/testing/testing_api_cmd_patch_instance.c index 59dcac07..b3a2865c 100644 --- a/src/testing/testing_api_cmd_patch_instance.c +++ b/src/testing/testing_api_cmd_patch_instance.c @@ -55,16 +55,6 @@ struct PatchInstanceState const char *instance_id; /** - * Length of the @payto_uris array - */ - unsigned int payto_uris_length; - - /** - * Array of payto URIs. - */ - const char **payto_uris; - - /** * Name of the instance. */ const char *name; @@ -161,21 +151,12 @@ patch_instance_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct PatchInstanceState *pis = cls; - struct TALER_MERCHANT_AccountConfig accounts[GNUNET_NZL ( - pis->payto_uris_length)]; - - memset (accounts, - 0, - sizeof (accounts)); - for (unsigned int i = 0; i<pis->payto_uris_length; i++) - accounts[i].payto_uri = pis->payto_uris[i]; + pis->is = is; pis->iph = TALER_MERCHANT_instance_patch ( TALER_TESTING_interpreter_get_context (is), pis->merchant_url, pis->instance_id, - pis->payto_uris_length, - accounts, pis->name, TALER_KYCLOGIC_KYC_UT_BUSINESS, pis->address, @@ -199,39 +180,23 @@ patch_instance_run (void *cls, * @param index index number of the object to extract. * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue patch_instance_traits (void *cls, const void **ret, const char *trait, unsigned int index) { struct PatchInstanceState *pis = cls; - #define NUM_TRAITS (pis->payto_uris_length) + 9 - struct TALER_TESTING_Trait traits[NUM_TRAITS]; - traits[0] = - TALER_TESTING_make_trait_instance_name (pis->name); - traits[1] = - TALER_TESTING_make_trait_instance_id (pis->instance_id); - traits[2] = - TALER_TESTING_make_trait_address (pis->address); - traits[3] = - TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction); - traits[4] = - TALER_TESTING_make_trait_use_stefan (&pis->use_stefan); - traits[5] = - TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay); - traits[6] = - TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay); - traits[7] = - TALER_TESTING_make_trait_payto_length (&pis->payto_uris_length); - traits[NUM_TRAITS - 1] = - TALER_TESTING_trait_end (); - for (unsigned int i = 0; i < pis->payto_uris_length; ++i) - { - traits[10 + i] = - TALER_TESTING_make_trait_payto_uris (i, - pis->payto_uris[i]); - } + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_instance_name (pis->name), + TALER_TESTING_make_trait_instance_id (pis->instance_id), + TALER_TESTING_make_trait_address (pis->address), + TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction), + TALER_TESTING_make_trait_use_stefan (&pis->use_stefan), + TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay), + TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay), + TALER_TESTING_trait_end () + }; return TALER_TESTING_get_trait (traits, ret, @@ -259,9 +224,7 @@ patch_instance_cleanup (void *cls, "PATCH /instance/$ID operation did not complete\n"); TALER_MERCHANT_instance_patch_cancel (pis->iph); } - json_decref (pis->address); json_decref (pis->jurisdiction); - GNUNET_free (pis->payto_uris); GNUNET_free (pis); } @@ -271,8 +234,6 @@ TALER_TESTING_cmd_merchant_patch_instance ( const char *label, const char *merchant_url, const char *instance_id, - unsigned int payto_uris_length, - const char *payto_uris[], const char *name, json_t *address, json_t *jurisdiction, @@ -287,12 +248,6 @@ TALER_TESTING_cmd_merchant_patch_instance ( pis->merchant_url = merchant_url; pis->instance_id = instance_id; pis->http_status = http_status; - pis->payto_uris_length = payto_uris_length; - pis->payto_uris = GNUNET_new_array (payto_uris_length, - const char *); - GNUNET_memcpy (pis->payto_uris, - payto_uris, - sizeof (const char *) * payto_uris_length); pis->name = name; pis->address = address; /* ownership transfer! */ pis->jurisdiction = jurisdiction; /* ownership transfer! */ diff --git a/src/testing/testing_api_cmd_patch_otp_device.c b/src/testing/testing_api_cmd_patch_otp_device.c new file mode 100644 index 00000000..ce263908 --- /dev/null +++ b/src/testing/testing_api_cmd_patch_otp_device.c @@ -0,0 +1,250 @@ +/* + This file is part of TALER + 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 Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing_api_cmd_patch_otp_device.c + * @brief command to test PATCH /otp-device + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "PATCH /otp-device" CMD. + */ +struct PatchOtpDeviceState +{ + + /** + * Handle for a "GET otp_device" request. + */ + struct TALER_MERCHANT_OtpDevicePatchHandle *iph; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the otp_device to run GET for. + */ + const char *otp_device_id; + + /** + * description of the otp_device + */ + const char *otp_device_description; + + /** + * base64-encoded key + */ + char *otp_key; + + /** + * Algorithm used by the OTP device + */ + enum TALER_MerchantConfirmationAlgorithm otp_alg; + + /** + * Counter of the device (if in counter mode). + */ + uint64_t otp_ctr; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a PATCH /otp-devices/$ID operation. + * + * @param cls closure for this function + * @param hr response being processed + */ +static void +patch_otp_device_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr) +{ + struct PatchOtpDeviceState *pis = cls; + + pis->iph = NULL; + if (pis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (pis->is)); + TALER_TESTING_interpreter_fail (pis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + break; + case MHD_HTTP_FORBIDDEN: + break; + case MHD_HTTP_NOT_FOUND: + break; + case MHD_HTTP_CONFLICT: + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status %u for PATCH /otp-devices/ID.\n", + hr->http_status); + } + TALER_TESTING_interpreter_next (pis->is); +} + + +/** + * Run the "PATCH /otp-devices/$ID" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +patch_otp_device_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PatchOtpDeviceState *pis = cls; + + pis->is = is; + pis->iph = TALER_MERCHANT_otp_device_patch ( + TALER_TESTING_interpreter_get_context (is), + pis->merchant_url, + pis->otp_device_id, + pis->otp_device_description, + pis->otp_key, + pis->otp_alg, + pis->otp_ctr, + &patch_otp_device_cb, + pis); + GNUNET_assert (NULL != pis->iph); +} + + +/** + * Offers information from the PATCH /otp-devices CMD state to other + * commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to extract. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +patch_otp_device_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct PatchOtpDeviceState *pts = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_otp_device_description (pts->otp_device_description), + TALER_TESTING_make_trait_otp_key (pts->otp_key), + TALER_TESTING_make_trait_otp_alg (&pts->otp_alg), + TALER_TESTING_make_trait_otp_id (pts->otp_device_id), + TALER_TESTING_trait_end (), + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Free the state of a "GET otp_device" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +patch_otp_device_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PatchOtpDeviceState *pis = cls; + + if (NULL != pis->iph) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "PATCH /otp-devices/$ID operation did not complete\n"); + TALER_MERCHANT_otp_device_patch_cancel (pis->iph); + } + GNUNET_free (pis->otp_key); + GNUNET_free (pis); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_patch_otp_device ( + const char *label, + const char *merchant_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + const enum TALER_MerchantConfirmationAlgorithm otp_alg, + uint64_t otp_ctr, + unsigned int http_status) +{ + struct PatchOtpDeviceState *pis; + + pis = GNUNET_new (struct PatchOtpDeviceState); + pis->merchant_url = merchant_url; + pis->otp_device_id = otp_device_id; + pis->http_status = http_status; + pis->otp_device_description = otp_device_description; + pis->otp_key = GNUNET_strdup (otp_key); + pis->otp_alg = otp_alg; + pis->otp_ctr = otp_ctr; + { + struct TALER_TESTING_Command cmd = { + .cls = pis, + .label = label, + .run = &patch_otp_device_run, + .cleanup = &patch_otp_device_cleanup, + .traits = &patch_otp_device_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_patch_otp_device.c */ diff --git a/src/testing/testing_api_cmd_patch_template.c b/src/testing/testing_api_cmd_patch_template.c index caf3bf23..8ad9d9dc 100644 --- a/src/testing/testing_api_cmd_patch_template.c +++ b/src/testing/testing_api_cmd_patch_template.c @@ -60,14 +60,9 @@ struct PatchTemplateState const char *template_description; /** - * base64-encoded key + * OTP device ID */ - char *pos_key; - - /** - * Option that add amount of the order - */ - enum TALER_MerchantConfirmationAlgorithm pos_alg; + char *otp_id; /** * Contract of the company @@ -147,8 +142,7 @@ patch_template_run (void *cls, pis->merchant_url, pis->template_id, pis->template_description, - pis->pos_key, - pis->pos_alg, + pis->otp_id, pis->template_contract, &patch_template_cb, pis); @@ -175,8 +169,7 @@ patch_template_traits (void *cls, struct PatchTemplateState *pts = cls; struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_template_description (pts->template_description), - TALER_TESTING_make_trait_template_pos_key (pts->pos_key), - TALER_TESTING_make_trait_template_pos_alg (&pts->pos_alg), + TALER_TESTING_make_trait_otp_id (pts->otp_id), TALER_TESTING_make_trait_template_contract (pts->template_contract), TALER_TESTING_make_trait_template_id (pts->template_id), TALER_TESTING_trait_end (), @@ -208,7 +201,7 @@ patch_template_cleanup (void *cls, "PATCH /templates/$ID operation did not complete\n"); TALER_MERCHANT_template_patch_cancel (pis->iph); } - GNUNET_free (pis->pos_key); + GNUNET_free (pis->otp_id); json_decref (pis->template_contract); GNUNET_free (pis); } @@ -220,8 +213,7 @@ TALER_TESTING_cmd_merchant_patch_template ( const char *merchant_url, const char *template_id, const char *template_description, - const char *pos_key, - const enum TALER_MerchantConfirmationAlgorithm pos_alg, + const char *otp_id, json_t *template_contract, unsigned int http_status) { @@ -232,8 +224,7 @@ TALER_TESTING_cmd_merchant_patch_template ( pis->template_id = template_id; pis->http_status = http_status; pis->template_description = template_description; - pis->pos_key = (NULL == pos_key) ? NULL : GNUNET_strdup (pos_key); - pis->pos_alg = pos_alg; + pis->otp_id = (NULL == otp_id) ? NULL : GNUNET_strdup (otp_id); pis->template_contract = template_contract; /* ownership taken */ { struct TALER_TESTING_Command cmd = { diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c index b49dd716..efc94a80 100644 --- a/src/testing/testing_api_cmd_pay_order.c +++ b/src/testing/testing_api_cmd_pay_order.c @@ -349,12 +349,13 @@ pay_run (void *cls, if (NULL == contract_terms) TALER_TESTING_FAIL (is); if (GNUNET_OK != - TALER_TESTING_get_trait_template_pos_key (proposal_cmd, - &ps->pos_key)) + TALER_TESTING_get_trait_otp_key (proposal_cmd, + &ps->pos_key)) ps->pos_key = NULL; - if (GNUNET_OK == - TALER_TESTING_get_trait_template_pos_alg (proposal_cmd, - &alg_ptr)) + if ( (GNUNET_OK == + TALER_TESTING_get_trait_otp_alg (proposal_cmd, + &alg_ptr)) && + (NULL != alg_ptr) ) ps->pos_alg = *alg_ptr; { /* Get information that needs to be put verbatim in the @@ -543,8 +544,8 @@ pay_traits (void *cls, TALER_TESTING_make_trait_merchant_pub (merchant_pub), TALER_TESTING_make_trait_merchant_sig (&ps->merchant_sig), TALER_TESTING_make_trait_amount (&amount_with_fee), - TALER_TESTING_make_trait_template_pos_key (ps->pos_key), - TALER_TESTING_make_trait_template_pos_alg (&ps->pos_alg), + TALER_TESTING_make_trait_otp_key (ps->pos_key), + TALER_TESTING_make_trait_otp_alg (&ps->pos_alg), TALER_TESTING_trait_end () }; diff --git a/src/testing/testing_api_cmd_post_account.c b/src/testing/testing_api_cmd_post_account.c index 345e869f..8ddad94c 100644 --- a/src/testing/testing_api_cmd_post_account.c +++ b/src/testing/testing_api_cmd_post_account.c @@ -37,7 +37,7 @@ struct PostAccountState /** * Handle for a "GET product" request. */ - struct TALER_MERCHANT_AccountPostHandle *aph; + struct TALER_MERCHANT_AccountsPostHandle *aph; /** * The interpreter state. @@ -50,9 +50,24 @@ struct PostAccountState const char *merchant_url; /** - * Account configuration for the account to create. + * Wire hash of the created account, set on success. */ - struct TALER_MERCHANT_AccountConfig ac; + struct TALER_MerchantWireHashP h_wire; + + /** + * RFC 8905 URI for the account to create. + */ + char *payto_uri; + + /** + * Credit facade URL for the account to create. + */ + char *credit_facade_url; + + /** + * Credit facade credentials for the account to create. + */ + json_t *credit_facade_credentials; /** * Expected HTTP response code. @@ -70,7 +85,7 @@ struct PostAccountState */ static void post_account_cb (void *cls, - const struct TALER_MERCHANT_AccountPostResponse *apr) + const struct TALER_MERCHANT_AccountsPostResponse *apr) { struct PostAccountState *pas = cls; @@ -87,7 +102,8 @@ post_account_cb (void *cls, } switch (apr->hr.http_status) { - case MHD_HTTP_NO_CONTENT: + case MHD_HTTP_OK: + pas->h_wire = apr->details.ok.h_wire; break; case MHD_HTTP_UNAUTHORIZED: break; @@ -123,10 +139,12 @@ post_account_run (void *cls, struct PostAccountState *pas = cls; pas->is = is; - pas->aph = TALER_MERCHANT_account_post ( + pas->aph = TALER_MERCHANT_accounts_post ( TALER_TESTING_interpreter_get_context (is), pas->merchant_url, - &pas->ac, + pas->payto_uri, + pas->credit_facade_url, + pas->credit_facade_credentials, &post_account_cb, pas); GNUNET_assert (NULL != pas->aph); @@ -149,8 +167,16 @@ post_account_traits (void *cls, const char *trait, unsigned int index) { - /* struct PostAccountState *pps = cls; */ + struct PostAccountState *pps = cls; struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_h_wires ( + 0, + &pps->h_wire), + TALER_TESTING_make_trait_payto_uris ( + 0, + pps->payto_uri), + TALER_TESTING_make_trait_merchant_base_url ( + pps->merchant_url), TALER_TESTING_trait_end (), }; @@ -178,9 +204,11 @@ post_account_cleanup (void *cls, { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "POST /account operation did not complete\n"); - TALER_MERCHANT_account_post_cancel (pas->aph); + TALER_MERCHANT_accounts_post_cancel (pas->aph); } - json_decref (pas->ac.credit_facade_credentials); + GNUNET_free (pas->payto_uri); + GNUNET_free (pas->credit_facade_url); + json_decref (pas->credit_facade_credentials); GNUNET_free (pas); } @@ -198,10 +226,11 @@ TALER_TESTING_cmd_merchant_post_account ( pas = GNUNET_new (struct PostAccountState); pas->merchant_url = merchant_url; - pas->ac.payto_uri = payto_uri; - pas->ac.credit_facade_url = credit_facade_url; + pas->payto_uri = GNUNET_strdup (payto_uri); + if (NULL != credit_facade_url) + pas->credit_facade_url = GNUNET_strdup (credit_facade_url); if (NULL != credit_facade_credentials) - pas->ac.credit_facade_credentials + pas->credit_facade_credentials = json_incref ((json_t *) credit_facade_credentials); pas->http_status = http_status; { diff --git a/src/testing/testing_api_cmd_post_instances.c b/src/testing/testing_api_cmd_post_instances.c index f90aacaf..f1b81cbf 100644 --- a/src/testing/testing_api_cmd_post_instances.c +++ b/src/testing/testing_api_cmd_post_instances.c @@ -55,16 +55,6 @@ struct PostInstancesState const char *instance_id; /** - * Length of the @payto_uris array - */ - unsigned int payto_uris_length; - - /** - * Array of payto URIs. - */ - const char **payto_uris; - - /** * Name of the instance. */ const char *name; @@ -168,21 +158,12 @@ post_instances_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct PostInstancesState *pis = cls; - struct TALER_MERCHANT_AccountConfig accounts[GNUNET_NZL ( - pis->payto_uris_length)]; - - memset (accounts, - 0, - sizeof (accounts)); - for (unsigned int i = 0; i<pis->payto_uris_length; i++) - accounts[i].payto_uri = pis->payto_uris[i]; + pis->is = is; pis->iph = TALER_MERCHANT_instances_post ( TALER_TESTING_interpreter_get_context (is), pis->merchant_url, pis->instance_id, - pis->payto_uris_length, - accounts, pis->name, TALER_KYCLOGIC_KYC_UT_BUSINESS, pis->address, @@ -219,32 +200,16 @@ post_instances_traits (void *cls, unsigned int index) { struct PostInstancesState *pis = cls; - #define NUM_TRAITS (pis->payto_uris_length) + 9 - struct TALER_TESTING_Trait traits[NUM_TRAITS]; - traits[0] = - TALER_TESTING_make_trait_instance_name (pis->name); - traits[1] = - TALER_TESTING_make_trait_instance_id (pis->instance_id); - traits[2] = - TALER_TESTING_make_trait_address (pis->address); - traits[3] = - TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction); - traits[4] = - TALER_TESTING_make_trait_use_stefan (&pis->use_stefan); - traits[5] = - TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay); - traits[6] = - TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay); - traits[7] = - TALER_TESTING_make_trait_payto_length (&pis->payto_uris_length); - traits[NUM_TRAITS - 1] = - TALER_TESTING_trait_end (); - for (unsigned int i = 0; i < pis->payto_uris_length; ++i) - { - traits[10 + i] = - TALER_TESTING_make_trait_payto_uris (i, - pis->payto_uris[i]); - } + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_instance_name (pis->name), + TALER_TESTING_make_trait_instance_id (pis->instance_id), + TALER_TESTING_make_trait_address (pis->address), + TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction), + TALER_TESTING_make_trait_use_stefan (&pis->use_stefan), + TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay), + TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay), + TALER_TESTING_trait_end () + }; return TALER_TESTING_get_trait (traits, ret, @@ -274,7 +239,6 @@ post_instances_cleanup (void *cls, } json_decref (pis->address); json_decref (pis->jurisdiction); - GNUNET_free (pis->payto_uris); GNUNET_free (pis); } @@ -284,8 +248,6 @@ TALER_TESTING_cmd_merchant_post_instances2 ( const char *label, const char *merchant_url, const char *instance_id, - unsigned int payto_uris_length, - const char *payto_uris[], const char *name, json_t *address, json_t *jurisdiction, @@ -301,12 +263,6 @@ TALER_TESTING_cmd_merchant_post_instances2 ( pis->merchant_url = merchant_url; pis->instance_id = instance_id; pis->http_status = http_status; - pis->payto_uris_length = payto_uris_length; - pis->payto_uris = GNUNET_new_array (payto_uris_length, - const char *); - GNUNET_memcpy (pis->payto_uris, - payto_uris, - sizeof (const char *) * payto_uris_length); pis->name = name; pis->address = address; /* ownership transfer! */ pis->jurisdiction = jurisdiction; /* ownership transfer! */ @@ -332,19 +288,12 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_merchant_post_instances (const char *label, const char *merchant_url, const char *instance_id, - const char *payto_uri, unsigned int http_status) { - const char *payto_uris[] = { - payto_uri - }; - return TALER_TESTING_cmd_merchant_post_instances2 ( label, merchant_url, instance_id, - 1, - payto_uris, instance_id, json_pack ("{s:s}", "city", "shopcity"), json_pack ("{s:s}", "city", "lawyercity"), diff --git a/src/testing/testing_api_cmd_post_otp_devices.c b/src/testing/testing_api_cmd_post_otp_devices.c new file mode 100644 index 00000000..09358274 --- /dev/null +++ b/src/testing/testing_api_cmd_post_otp_devices.c @@ -0,0 +1,256 @@ +/* + This file is part of TALER + 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 Foundation; either version 3, or + (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file testing_api_cmd_post_otp_devices.c + * @brief command to test POST /otp-devices + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_exchange_service.h> +#include <taler/taler_testing_lib.h> +#include "taler_merchant_service.h" +#include "taler_merchant_testing_lib.h" + + +/** + * State of a "POST /otp-devices" CMD. + */ +struct PostOtpDevicesState +{ + + /** + * Handle for a "GET otp_device" request. + */ + struct TALER_MERCHANT_OtpDevicesPostHandle *iph; + + /** + * The interpreter state. + */ + struct TALER_TESTING_Interpreter *is; + + /** + * Base URL of the merchant serving the request. + */ + const char *merchant_url; + + /** + * ID of the otp_device to run POST for. + */ + const char *otp_device_id; + + /** + * description of the otp_device + */ + const char *otp_device_description; + + /** + * base64-encoded key + */ + char *otp_key; + + /** + * Option that add amount of the order + */ + enum TALER_MerchantConfirmationAlgorithm otp_alg; + + /** + * Counter at the OTP device. + */ + uint64_t otp_ctr; + + /** + * Expected HTTP response code. + */ + unsigned int http_status; + +}; + + +/** + * Callback for a POST /otp-devices operation. + * + * @param cls closure for this function + * @param hr response being processed + */ +static void +post_otp_devices_cb (void *cls, + const struct TALER_MERCHANT_HttpResponse *hr) +{ + struct PostOtpDevicesState *tis = cls; + + tis->iph = NULL; + if (tis->http_status != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u (%d) to command %s\n", + hr->http_status, + (int) hr->ec, + TALER_TESTING_interpreter_get_current_label (tis->is)); + TALER_TESTING_interpreter_fail (tis->is); + return; + } + switch (hr->http_status) + { + case MHD_HTTP_NO_CONTENT: + break; + case MHD_HTTP_UNAUTHORIZED: + break; + case MHD_HTTP_FORBIDDEN: + break; + case MHD_HTTP_NOT_FOUND: + break; + case MHD_HTTP_CONFLICT: + break; + default: + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unhandled HTTP status %u for POST /otp-devices.\n", + hr->http_status); + } + TALER_TESTING_interpreter_next (tis->is); +} + + +/** + * Run the "POST /otp-devices" CMD. + * + * + * @param cls closure. + * @param cmd command being run now. + * @param is interpreter state. + */ +static void +post_otp_devices_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct PostOtpDevicesState *tis = cls; + + tis->is = is; + tis->iph = TALER_MERCHANT_otp_devices_post ( + TALER_TESTING_interpreter_get_context (is), + tis->merchant_url, + tis->otp_device_id, + tis->otp_device_description, + tis->otp_key, + tis->otp_alg, + tis->otp_ctr, + &post_otp_devices_cb, + tis); + if (NULL == tis->iph) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (tis->is); + return; + } +} + + +/** + * Offers information from the POST /otp-devices CMD state to other + * commands. + * + * @param cls closure + * @param[out] ret result (could be anything) + * @param trait name of the trait + * @param index index number of the object to extract. + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +post_otp_devices_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct PostOtpDevicesState *pts = cls; + struct TALER_TESTING_Trait traits[] = { + TALER_TESTING_make_trait_otp_device_description (pts->otp_device_description), + TALER_TESTING_make_trait_otp_key (pts->otp_key), + TALER_TESTING_make_trait_otp_alg (&pts->otp_alg), + TALER_TESTING_make_trait_otp_id (pts->otp_device_id), + TALER_TESTING_trait_end (), + }; + + return TALER_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +/** + * Free the state of a "POST otp_device" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd command being run. + */ +static void +post_otp_devices_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct PostOtpDevicesState *tis = cls; + + if (NULL != tis->iph) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "POST /otp-devices operation did not complete\n"); + TALER_MERCHANT_otp_devices_post_cancel (tis->iph); + } + GNUNET_free (tis->otp_key); + GNUNET_free (tis); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_merchant_post_otp_devices ( + const char *label, + const char *merchant_url, + const char *otp_device_id, + const char *otp_device_description, + const char *otp_key, + const enum TALER_MerchantConfirmationAlgorithm otp_alg, + uint64_t otp_ctr, + unsigned int http_status) +{ + struct PostOtpDevicesState *tis; + + tis = GNUNET_new (struct PostOtpDevicesState); + tis->merchant_url = merchant_url; + tis->otp_device_id = otp_device_id; + tis->http_status = http_status; + tis->otp_device_description = otp_device_description; + tis->otp_key = GNUNET_strdup (otp_key); + tis->otp_alg = otp_alg; + tis->otp_ctr = otp_ctr; + { + struct TALER_TESTING_Command cmd = { + .cls = tis, + .label = label, + .run = &post_otp_devices_run, + .cleanup = &post_otp_devices_cleanup, + .traits = &post_otp_devices_traits + }; + + return cmd; + } +} + + +/* end of testing_api_cmd_post_otp_devices.c */ diff --git a/src/testing/testing_api_cmd_post_templates.c b/src/testing/testing_api_cmd_post_templates.c index 13ffc24e..8c8cd8ca 100644 --- a/src/testing/testing_api_cmd_post_templates.c +++ b/src/testing/testing_api_cmd_post_templates.c @@ -60,14 +60,9 @@ struct PostTemplatesState const char *template_description; /** - * base64-encoded key + * OTP device ID. */ - char *pos_key; - - /** - * Option that add amount of the order - */ - enum TALER_MerchantConfirmationAlgorithm pos_alg; + char *otp_id; /** * Contract of the company @@ -148,8 +143,7 @@ post_templates_run (void *cls, tis->merchant_url, tis->template_id, tis->template_description, - tis->pos_key, - tis->pos_alg, + tis->otp_id, tis->template_contract, &post_templates_cb, tis); @@ -181,8 +175,7 @@ post_templates_traits (void *cls, struct PostTemplatesState *pts = cls; struct TALER_TESTING_Trait traits[] = { TALER_TESTING_make_trait_template_description (pts->template_description), - TALER_TESTING_make_trait_template_pos_key (pts->pos_key), - TALER_TESTING_make_trait_template_pos_alg (&pts->pos_alg), + TALER_TESTING_make_trait_otp_id (pts->otp_id), TALER_TESTING_make_trait_template_contract (pts->template_contract), TALER_TESTING_make_trait_template_id (pts->template_id), TALER_TESTING_trait_end (), @@ -214,7 +207,7 @@ post_templates_cleanup (void *cls, "POST /templates operation did not complete\n"); TALER_MERCHANT_templates_post_cancel (tis->iph); } - GNUNET_free (tis->pos_key); + GNUNET_free (tis->otp_id); json_decref (tis->template_contract); GNUNET_free (tis); } @@ -226,8 +219,7 @@ TALER_TESTING_cmd_merchant_post_templates2 ( const char *merchant_url, const char *template_id, const char *template_description, - const char *pos_key, - const enum TALER_MerchantConfirmationAlgorithm pos_alg, + const char *otp_id, json_t *template_contract, unsigned int http_status) { @@ -241,8 +233,7 @@ TALER_TESTING_cmd_merchant_post_templates2 ( tis->template_id = template_id; tis->http_status = http_status; tis->template_description = template_description; - tis->pos_key = (NULL == pos_key) ? NULL : GNUNET_strdup (pos_key); - tis->pos_alg = pos_alg; + tis->otp_id = (NULL == otp_id) ? NULL : GNUNET_strdup (otp_id); tis->template_contract = template_contract; { struct TALER_TESTING_Command cmd = { @@ -271,7 +262,6 @@ TALER_TESTING_cmd_merchant_post_templates (const char *label, template_id, template_description, NULL, - TALER_MCA_NONE, GNUNET_JSON_PACK ( GNUNET_JSON_pack_uint64 ("minimum_age", 0), GNUNET_JSON_pack_time_rel ("pay_duration", diff --git a/src/testing/testing_api_cmd_post_using_templates.c b/src/testing/testing_api_cmd_post_using_templates.c index da45135b..7aeec33d 100644 --- a/src/testing/testing_api_cmd_post_using_templates.c +++ b/src/testing/testing_api_cmd_post_using_templates.c @@ -133,14 +133,19 @@ struct PostUsingTemplatesState const char *duplicate_of; /** + * Label of command creating/updating OTP device, or NULL. + */ + const char *otp_ref; + + /** * Encoded key for the payment verification. */ - const char *template_pos_key; + const char *otp_key; /** * Option that add amount of the order */ - const enum TALER_MerchantConfirmationAlgorithm *template_pos_alg; + const enum TALER_MerchantConfirmationAlgorithm *otp_alg; /** * Expected HTTP response code. @@ -358,16 +363,19 @@ post_using_templates_run (void *cls, TALER_TESTING_get_trait_template_id (ref, &template_id)) TALER_TESTING_FAIL (is); - - if (GNUNET_OK != - TALER_TESTING_get_trait_template_pos_key (ref, - &tis->template_pos_key)) - TALER_TESTING_FAIL (is); - - if (GNUNET_OK != - TALER_TESTING_get_trait_template_pos_alg (ref, - &tis->template_pos_alg)) - TALER_TESTING_FAIL (is); + if (NULL != tis->otp_ref) + { + ref = TALER_TESTING_interpreter_lookup_command (is, + tis->otp_ref); + if (GNUNET_OK != + TALER_TESTING_get_trait_otp_key (ref, + &tis->otp_key)) + TALER_TESTING_FAIL (is); + if (GNUNET_OK != + TALER_TESTING_get_trait_otp_alg (ref, + &tis->otp_alg)) + TALER_TESTING_FAIL (is); + } tis->iph = TALER_MERCHANT_using_templates_post ( TALER_TESTING_interpreter_get_context (is), tis->merchant_url, @@ -408,8 +416,8 @@ post_using_templates_traits (void *cls, TALER_TESTING_make_trait_merchant_pub (&pts->merchant_pub), TALER_TESTING_make_trait_claim_nonce (&pts->nonce), TALER_TESTING_make_trait_claim_token (&pts->claim_token), - TALER_TESTING_make_trait_template_pos_key (pts->template_pos_key), - TALER_TESTING_make_trait_template_pos_alg (pts->template_pos_alg), + TALER_TESTING_make_trait_otp_key (pts->otp_key), + TALER_TESTING_make_trait_otp_alg (pts->otp_alg), TALER_TESTING_trait_end (), }; @@ -554,6 +562,7 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_merchant_post_using_templates ( const char *label, const char *template_ref, + const char *otp_ref, const char *merchant_url, const char *using_template_id, const char *summary, @@ -566,6 +575,7 @@ TALER_TESTING_cmd_merchant_post_using_templates ( tis = GNUNET_new (struct PostUsingTemplatesState); tis->template_ref = template_ref; + tis->otp_ref = otp_ref; tis->merchant_url = merchant_url; tis->using_template_id = using_template_id; tis->http_status = http_status; |