diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-03-04 10:40:50 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-03-04 10:40:50 +0100 |
commit | c2a9a820522f8f44505ecf5a46149515c6a87a03 (patch) | |
tree | 4be9c43a4c525fc2e9b4ea13703badad237a4b22 | |
parent | 1e6bd4db09bebef2f65a88829d4637cd3017adb1 (diff) | |
parent | 950f7cd53ef2457494ea98a06be667f06cd3f1e8 (diff) | |
download | merchant-c2a9a820522f8f44505ecf5a46149515c6a87a03.tar.gz merchant-c2a9a820522f8f44505ecf5a46149515c6a87a03.tar.bz2 merchant-c2a9a820522f8f44505ecf5a46149515c6a87a03.zip |
Merge branch 'master' of git+ssh://git.taler.net/merchant
22 files changed, 480 insertions, 131 deletions
@@ -80,3 +80,4 @@ doc/stamp-? doc/stamp-vti doc/mdate-sh doc/texinfo.tex +.private-key diff --git a/src/.gitignore b/src/.gitignore index 90ea1a04..4f49c073 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -6,4 +6,5 @@ *.log *.trs */__pycache__ -test-*
\ No newline at end of file +test-* +!test-*.sh diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 4c05db42..3dd126a6 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -66,6 +66,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_private-patch-products-ID.h \ taler-merchant-httpd_private-post-instances.c \ taler-merchant-httpd_private-post-instances.h \ + taler-merchant-httpd_private-post-instances-ID-auth.c \ + taler-merchant-httpd_private-post-instances-ID-auth.h \ taler-merchant-httpd_private-post-products.c \ taler-merchant-httpd_private-post-products.h \ taler-merchant-httpd_private-post-products-ID-lock.c \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 45d7ed07..5519f517 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -49,6 +49,7 @@ #include "taler-merchant-httpd_private-patch-orders-ID-forget.h" #include "taler-merchant-httpd_private-patch-products-ID.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-orders.h" #include "taler-merchant-httpd_private-post-orders-ID-refund.h" #include "taler-merchant-httpd_private-post-products.h" @@ -774,6 +775,7 @@ handle_mhd_completion_callback (void *cls, if (NULL == hc) return; + GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Finished handling request for `%s' with MHD termination code %d\n", hc->url, @@ -786,6 +788,9 @@ handle_mhd_completion_callback (void *cls, json_decref (hc->request_body); if (NULL != hc->instance) TMH_instance_decref (hc->instance); + memset (&hc->async_scope_id, + 0, + sizeof (struct GNUNET_AsyncScopeId)); GNUNET_free (hc); *con_cls = NULL; } @@ -955,6 +960,35 @@ TMH_add_instance (struct TMH_MerchantInstance *mi) return ret; } +/** + * Extract the token from authorization header value @a auth. + * + * @param auth pointer to authorization header value, + * will be updated to point to the start of the token + * or set to NULL if header value is invalid + */ +static void +extract_token (const char **auth) +{ + const char *bearer = "Bearer "; + const char *tok = *auth; + if (0 != strncmp (tok, bearer, strlen (bearer))) + { + *auth = NULL; + return; + } + tok = tok + strlen (bearer); + while (' ' == *tok) + tok++; + if (0 != strncasecmp (tok, + RFC_8959_PREFIX, + strlen (RFC_8959_PREFIX))) + { + *auth = NULL; + return; + } + *auth = tok; +} /** * A client has requested the given url using the given method @@ -1049,6 +1083,14 @@ url_handler (void *cls, in the code... */ .max_upload = 1024 * 1024 * 8 }, + /* POST /auth: */ + { + .url_prefix = "/auth", + .method = MHD_HTTP_METHOD_POST, + .handler = &TMH_private_post_instances_ID_auth, + /* Body should be pretty small. */ + .max_upload = 1024 * 1024, + }, /* GET /products: */ { .url_prefix = "/products", @@ -1490,8 +1532,8 @@ url_handler (void *cls, { /* Override default instance access control */ TMH_compute_auth (default_auth, - &hc->instance->settings.auth_salt, - &hc->instance->settings.auth_hash); + &hc->instance->auth.auth_salt, + &hc->instance->auth.auth_hash); GNUNET_free (default_auth); } } @@ -1508,6 +1550,7 @@ url_handler (void *cls, { handlers = private_handlers; url += strlen (private_prefix) - 1; + use_private = true; } else { @@ -1641,23 +1684,15 @@ url_handler (void *cls, MHD_HTTP_HEADER_AUTHORIZATION); if (NULL != auth) { - if (0 != strncasecmp (auth, - RFC_8959_PREFIX, - strlen (RFC_8959_PREFIX))) - { - /* We _only_ complain about malformed auth headers if - authorization was truly required (#6737). This helps - in case authorization was disabled in the backend - because some reverse proxy is already doing it, and - then that reverse proxy may forward malformed auth - headers to the backend. */ + /* We _only_ complain about malformed auth headers if + authorization was truly required (#6737). This helps + in case authorization was disabled in the backend + because some reverse proxy is already doing it, and + then that reverse proxy may forward malformed auth + headers to the backend. */ + extract_token (&auth); + if (NULL == auth) auth_malformed = true; - auth = NULL; - } - else - { - auth += strlen (RFC_8959_PREFIX); - } } /* Are the credentials provided OK for the default instance? @@ -1673,8 +1708,8 @@ url_handler (void *cls, (NULL == default_auth) ) auth_ok = (GNUNET_OK == TMH_check_auth (auth, - &def_instance->settings.auth_salt, - &def_instance->settings.auth_hash)); + &def_instance->auth.auth_salt, + &def_instance->auth.auth_hash)); /* Only permit 'default' auth if we are either working with the default instance OR patching/deleting an instance OR have no instance */ if ( (hc->rh->handler != &TMH_private_patch_instances_ID) && @@ -1687,8 +1722,8 @@ url_handler (void *cls, if (NULL != hc->instance) auth_ok |= (GNUNET_OK == TMH_check_auth (auth, - &hc->instance->settings.auth_salt, - &hc->instance->settings.auth_hash)); + &hc->instance->auth.auth_salt, + &hc->instance->auth.auth_hash)); if (! auth_ok) { if (auth_malformed) @@ -1773,6 +1808,7 @@ 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[]) { @@ -1782,6 +1818,7 @@ add_instance_cb (void *cls, GNUNET_assert (NULL != merchant_priv); mi = GNUNET_new (struct TMH_MerchantInstance); mi->settings = *is; + mi->auth = *ias; mi->settings.id = GNUNET_strdup (mi->settings.id); mi->settings.name = GNUNET_strdup (mi->settings.name); mi->settings.address = json_incref (mi->settings.address); diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h index 50badbb1..7cb3c836 100644 --- a/src/backend/taler-merchant-httpd.h +++ b/src/backend/taler-merchant-httpd.h @@ -133,6 +133,11 @@ struct TMH_MerchantInstance struct TALER_MERCHANTDB_InstanceSettings settings; /** + * General settings for an instance. + */ + struct TALER_MERCHANTDB_InstanceAuthSettings auth; + + /** * Reference counter on this structure. Only destroyed if the * counter hits zero. */ diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID.c b/src/backend/taler-merchant-httpd_private-get-instances-ID.c index a7bea350..8f392393 100644 --- a/src/backend/taler-merchant-httpd_private-get-instances-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-instances-ID.c @@ -68,7 +68,8 @@ TMH_private_get_instances_ID (const struct TMH_RequestHandler *rh, connection, MHD_HTTP_OK, "{s:o, s:s, s:o, s:O, s:O," - " s:o, s:o, s:I, s:o, s:o}", + " s:o, s:o, s:I, s:o, s:o," + " s:{ s:s } }", "accounts", ja, "name", @@ -95,7 +96,11 @@ TMH_private_get_instances_ID (const struct TMH_RequestHandler *rh, mi->settings.default_wire_transfer_delay), "default_pay_delay", GNUNET_JSON_from_time_rel ( - mi->settings.default_pay_delay)); + mi->settings.default_pay_delay), + /* end of second group of 5 */ + "auth", + "method", + GNUNET_is_zero (mi->auth.auth_hash.bits) ? "external" : "token"); } 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 637ded04..edda6228 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,10 @@ TMH_private_patch_instances_ID (const struct TMH_RequestHandler *rh, struct TMH_MerchantInstance *mi = hc->instance; struct TALER_MERCHANTDB_InstanceSettings is; json_t *payto_uris; - const char *auth_token = NULL; const char *name; struct TMH_WireMethod *wm_head = NULL; struct TMH_WireMethod *wm_tail = NULL; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("auth_token", - &auth_token)), GNUNET_JSON_spec_json ("payto_uris", &payto_uris), GNUNET_JSON_spec_string ("name", @@ -149,14 +145,6 @@ TMH_private_patch_instances_ID (const struct TMH_RequestHandler *rh, /* Check for equality of settings */ if (! ( (0 == strcmp (mi->settings.name, name)) && - ( ( (NULL != auth_token) && - (GNUNET_OK == - TMH_check_auth (auth_token, - &mi->settings.auth_salt, - &mi->settings.auth_hash)) ) || - ( (NULL == auth_token) && - (GNUNET_YES == - GNUNET_is_zero (&mi->settings.auth_hash))) ) && (1 == json_equal (mi->settings.address, is.address)) && (1 == json_equal (mi->settings.jurisdiction, @@ -404,21 +392,6 @@ giveup: json_decref (mi->settings.address); json_decref (mi->settings.jurisdiction); is.id = mi->settings.id; - if (NULL == auth_token) - { - memset (&is.auth_salt, - 0, - sizeof (is.auth_salt)); - memset (&is.auth_hash, - 0, - sizeof (is.auth_hash)); - } - else - { - TMH_compute_auth (auth_token, - &is.auth_salt, - &is.auth_hash); - } mi->settings = is; mi->settings.address = json_incref (mi->settings.address); mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction); diff --git a/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.c b/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.c new file mode 100644 index 00000000..8d8df93f --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.c @@ -0,0 +1,162 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems SA + + GNU 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. + + GNU 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-instances-ID-auth.c + * @brief implementing POST /instances/$ID/auth request handling + * @author Christian Grothoff + * @author Florian Dold + */ +#include "platform.h" +#include "taler-merchant-httpd_private-post-instances-ID-auth.h" +#include <taler/taler_json_lib.h> + + +/** + * How often do we retry the simple INSERT database transaction? + */ +#define MAX_RETRIES 3 + + +/** + * Change the authentication settings of an 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_post_instances_ID_auth (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + struct TALER_MERCHANTDB_InstanceAuthSettings ias; + const char *auth_token = NULL; + struct TMH_MerchantInstance *mi = hc->instance; + json_t *jauth = hc->request_body; + + { + bool auth_ok = false; + const char *auth_method = json_string_value (json_object_get (jauth, + "method")); + + if (NULL == auth_method) + GNUNET_break_op (0); + else if (0 == strcmp (auth_method, "external")) + { + auth_token = NULL; + auth_ok = true; + } + else if (0 == strcmp (auth_method, "token")) + { + auth_token = json_string_value (json_object_get (jauth, "token")); + if (NULL != auth_token) + { + if (0 != strncasecmp (RFC_8959_PREFIX, + auth_token, + strlen (RFC_8959_PREFIX))) + GNUNET_break_op (0); + else + auth_ok = true; + } + else + GNUNET_break_op (0); + } + + if (! auth_ok) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_PRIVATE_POST_INSTANCE_AUTH_BAD_AUTH, + "bad authentication config"); + } + } + + if (NULL == auth_token) + { + memset (&ias.auth_salt, + 0, + sizeof (ias.auth_salt)); + memset (&ias.auth_hash, + 0, + sizeof (ias.auth_hash)); + } + else + { + TMH_compute_auth (auth_token, + &ias.auth_salt, + &ias.auth_hash); + } + + { + enum GNUNET_DB_QueryStatus qs; + + for (unsigned int i = 0; i<MAX_RETRIES; i++) + { + if (GNUNET_OK != + TMH_db->start (TMH_db->cls, + "post /instances/$ID/auth")) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_START_FAILED, + NULL); + } + qs = TMH_db->update_instance_auth (TMH_db->cls, + mi->settings.id, + &ias); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + TMH_db->rollback (TMH_db->cls); + goto retry; + } + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + TMH_db->rollback (TMH_db->cls); + goto retry; + } + qs = TMH_db->commit (TMH_db->cls); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +retry: + if (GNUNET_DB_STATUS_SOFT_ERROR != qs) + break; /* success! -- or hard failure */ + } /* for .. MAX_RETRIES */ + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_COMMIT_FAILED, + NULL); + } + /* Finally, also update our running process */ + mi->auth = ias; + } + return TALER_MHD_reply_static (connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); +} + +/* end of taler-merchant-httpd_private-post-instances-ID-auth.c */ diff --git a/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.h b/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.h new file mode 100644 index 00000000..3a47c42c --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-post-instances-ID-auth.h @@ -0,0 +1,44 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems SA + + GNU 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. + + GNU 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-instances-ID-auth.h + * @brief implements POST /instances/$ID/auth request handling + * @author Christian Grothoff + * @author Florian Dold + */ +#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_INSTANCES_ID_AUTH_H +#define TALER_MERCHANT_HTTPD_PRIVATE_POST_INSTANCES_ID_AUTH_H +#include "taler-merchant-httpd.h" + + +/** + * Change the instance's auth settings. + * + * @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_instances_ID_auth (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +#endif diff --git a/src/backend/taler-merchant-httpd_private-post-instances.c b/src/backend/taler-merchant-httpd_private-post-instances.c index c4d1d617..88a51a26 100644 --- a/src/backend/taler-merchant-httpd_private-post-instances.c +++ b/src/backend/taler-merchant-httpd_private-post-instances.c @@ -141,14 +141,15 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, struct TMH_HandlerContext *hc) { struct TALER_MERCHANTDB_InstanceSettings is; + struct TALER_MERCHANTDB_InstanceAuthSettings ias; json_t *payto_uris; const char *auth_token = NULL; struct TMH_WireMethod *wm_head = NULL; struct TMH_WireMethod *wm_tail = NULL; + json_t *jauth; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("auth_token", - &auth_token)), + GNUNET_JSON_spec_json ("auth", + &jauth), GNUNET_JSON_spec_json ("payto_uris", &payto_uris), GNUNET_JSON_spec_string ("id", @@ -184,6 +185,47 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, : MHD_NO; } + + { + bool auth_ok = false; + const char *auth_method = json_string_value (json_object_get (jauth, + "method")); + + if (NULL == auth_method) + GNUNET_break_op (0); + else if (0 == strcmp (auth_method, "external")) + { + auth_token = NULL; + auth_ok = true; + } + else if (0 == strcmp (auth_method, "token")) + { + auth_token = json_string_value (json_object_get (jauth, "token")); + if (NULL != auth_token) + { + if (0 != strncasecmp (RFC_8959_PREFIX, + auth_token, + strlen (RFC_8959_PREFIX))) + GNUNET_break_op (0); + else + auth_ok = true; + } + else + GNUNET_break_op (0); + } + + if (! auth_ok) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH, + "bad authentication config"); + } + } + + if ((0 != strcasecmp (is.default_max_deposit_fee.currency, TMH_currency)) || (0 != strcasecmp (is.default_max_wire_fee.currency, @@ -212,11 +254,11 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, ( ( (NULL != auth_token) && (GNUNET_OK == TMH_check_auth (auth_token, - &mi->settings.auth_salt, - &mi->settings.auth_hash)) ) || + &mi->auth.auth_salt, + &mi->auth.auth_hash)) ) || ( (NULL == auth_token) && (GNUNET_YES == - GNUNET_is_zero (&mi->settings.auth_hash))) ) && + GNUNET_is_zero (&mi->auth.auth_hash))) ) && (1 == json_equal (mi->settings.address, is.address)) && (1 == json_equal (mi->settings.jurisdiction, @@ -357,18 +399,18 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, if (NULL == auth_token) { - memset (&is.auth_salt, + memset (&ias.auth_salt, 0, - sizeof (is.auth_salt)); - memset (&is.auth_hash, + sizeof (ias.auth_salt)); + memset (&ias.auth_hash, 0, - sizeof (is.auth_hash)); + sizeof (ias.auth_hash)); } else { TMH_compute_auth (auth_token, - &is.auth_salt, - &is.auth_hash); + &ias.auth_salt, + &ias.auth_hash); } { struct TMH_MerchantInstance *mi; @@ -402,7 +444,8 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, qs = TMH_db->insert_instance (TMH_db->cls, &mi->merchant_pub, &mi->merchant_priv, - &mi->settings); + &mi->settings, + &mi->auth); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 616172f1..a1b659db 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -343,6 +343,11 @@ struct LookupInstancesContext struct TALER_MERCHANTDB_InstanceSettings is; /** + * Instance authentication settings, valid only during find_instances_cb(). + */ + struct TALER_MERCHANTDB_InstanceAuthSettings ias; + + /** * Instance serial number, valid only during find_instances_cb(). */ uint64_t instance_serial; @@ -407,6 +412,7 @@ call_with_accounts (struct LookupInstancesContext *lic, &lic->merchant_pub, (0 == qs) ? NULL : &merchant_priv, &lic->is, + &lic->ias, num_accounts, accounts); } @@ -493,11 +499,11 @@ lookup_instances_cb (void *cls, &lic->merchant_pub), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_auto_from_type ("auth_hash", - &lic->is.auth_hash), + &lic->ias.auth_hash), &no_auth), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_auto_from_type ("auth_salt", - &lic->is.auth_salt), + &lic->ias.auth_salt), &no_salt), GNUNET_PQ_result_spec_string ("merchant_id", &lic->is.id), @@ -524,12 +530,12 @@ lookup_instances_cb (void *cls, GNUNET_PQ_query_param_end }; - memset (&lic->is.auth_salt, + memset (&lic->ias.auth_salt, 0, - sizeof (lic->is.auth_salt)); - memset (&lic->is.auth_hash, + sizeof (lic->ias.auth_salt)); + memset (&lic->ias.auth_hash, 0, - sizeof (lic->is.auth_hash)); + sizeof (lic->ias.auth_hash)); if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, @@ -610,13 +616,14 @@ static enum GNUNET_DB_QueryStatus postgres_insert_instance (void *cls, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPrivateKeyP *merchant_priv, - const struct TALER_MERCHANTDB_InstanceSettings *is) + const struct TALER_MERCHANTDB_InstanceSettings *is, + const struct TALER_MERCHANTDB_InstanceAuthSettings *ias) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (merchant_pub), - GNUNET_PQ_query_param_auto_from_type (&is->auth_hash), - GNUNET_PQ_query_param_auto_from_type (&is->auth_salt), + GNUNET_PQ_query_param_auto_from_type (&ias->auth_hash), + GNUNET_PQ_query_param_auto_from_type (&ias->auth_salt), GNUNET_PQ_query_param_string (is->id), GNUNET_PQ_query_param_string (is->name), TALER_PQ_query_param_json (is->address), @@ -753,6 +760,31 @@ postgres_update_instance (void *cls, 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_end + }; + + check_connection (pg); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "update_instance", + params); +} + +/** + * Update information about an instance's authentication settings + * into our database. + * + * @param cls closure + * @param ias details about the instance + * @return database result code + */ +static enum GNUNET_DB_QueryStatus +postgres_update_instance_auth (void *cls, + const char *merchant_id, + const struct TALER_MERCHANTDB_InstanceAuthSettings *is) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (merchant_id), GNUNET_PQ_query_param_auto_from_type (&is->auth_hash), GNUNET_PQ_query_param_auto_from_type (&is->auth_salt), GNUNET_PQ_query_param_end @@ -760,7 +792,7 @@ postgres_update_instance (void *cls, check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_instance", + "update_instance_auth", params); } @@ -6149,10 +6181,15 @@ postgres_connect (void *cls) ",default_wire_fee_amortization=$9" ",default_wire_transfer_delay=$10" ",default_pay_delay=$11" - ",auth_hash=$12" - ",auth_salt=$13" " WHERE merchant_id = $1", - 13), + 11), + /* for postgres_update_instance_auth() */ + GNUNET_PQ_make_prepare ("update_instance_auth", + "UPDATE merchant_instances SET" + " auth_hash=$2" + ",auth_salt=$3" + " WHERE merchant_id = $1", + 3), /* for postgres_inactivate_account() */ GNUNET_PQ_make_prepare ("inactivate_account", "UPDATE merchant_accounts SET" @@ -8487,6 +8524,7 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->delete_instance_private_key = &postgres_delete_instance_private_key; plugin->purge_instance = &postgres_purge_instance; plugin->update_instance = &postgres_update_instance; + plugin->update_instance_auth = &postgres_update_instance_auth; plugin->activate_account = &postgres_activate_account; plugin->inactivate_account = &postgres_inactivate_account; plugin->lookup_products = &postgres_lookup_products; diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index 9be7933d..be07c3cf 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -266,6 +266,7 @@ check_accounts_equal (const struct TALER_MERCHANTDB_AccountDetails *a, * @param merchant_pub public key of the instance * @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 */ @@ -274,6 +275,7 @@ 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[]) { @@ -324,11 +326,13 @@ static int test_insert_instance (const struct InstanceData *instance, enum GNUNET_DB_QueryStatus expected_result) { + struct TALER_MERCHANTDB_InstanceAuthSettings ias = { 0 }; TEST_COND_RET_ON_FAIL (expected_result == plugin->insert_instance (plugin->cls, &instance->merchant_pub, &instance->merchant_priv, - &instance->instance), + &instance->instance, + &ias), "Insert instance failed\n"); return 0; } diff --git a/src/include/platform.h b/src/include/platform.h index 70c296fd..ab260ebe 100644 --- a/src/include/platform.h +++ b/src/include/platform.h @@ -62,7 +62,7 @@ * Mark Nottingham thinks this should be fixed by revising HTTP * spec (https://github.com/httpwg/http-core/issues/733)) */ -#define RFC_8959_PREFIX "Bearer secret-token:" +#define RFC_8959_PREFIX "secret-token:" #endif /* PLATFORM_H_ */ diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 9937750b..8a8cc25b 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -503,7 +503,6 @@ typedef void * @param default_max_deposit_fee default maximum deposit fee merchant is willing to pay * @param default_wire_transfer_delay default wire transfer delay merchant will ask for * @param default_pay_delay default validity period for offers merchant makes - * @param auth_token authorization token needed to access the instance, can be NULL * @param cb function to call with the * backend's instances information * @param cb_cls closure for @a config_cb @@ -595,7 +594,6 @@ TALER_MERCHANT_instance_patch ( const struct TALER_Amount *default_max_deposit_fee, struct GNUNET_TIME_Relative default_wire_transfer_delay, struct GNUNET_TIME_Relative default_pay_delay, - const char *auth_token, TALER_MERCHANT_InstancePatchCallback cb, void *cb_cls); diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h index 4eafd6fa..2e872406 100644 --- a/src/include/taler_merchant_testing_lib.h +++ b/src/include/taler_merchant_testing_lib.h @@ -177,7 +177,6 @@ TALER_TESTING_cmd_merchant_post_instances2 ( * @param default_max_deposit_fee default maximum deposit fee merchant is willing to pay * @param default_wire_transfer_delay default wire transfer delay merchant will ask for * @param default_pay_delay default validity period for offers merchant makes - * @param auth_token authorization token needed to access the instance, can be NULL * @param http_status expected HTTP response code. * @return the command. */ @@ -196,7 +195,6 @@ TALER_TESTING_cmd_merchant_patch_instance ( const char *default_max_deposit_fee, struct GNUNET_TIME_Relative default_wire_transfer_delay, struct GNUNET_TIME_Relative default_pay_delay, - const char *auth_token, unsigned int http_status); diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 963f9234..6f94e514 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -60,6 +60,22 @@ struct TALER_MERCHANTDB_AccountDetails }; +/** + * Authentication settings for an instance. + */ +struct TALER_MERCHANTDB_InstanceAuthSettings +{ + /** + * Hash used for authentication. All zero if authentication is off. + */ + struct GNUNET_HashCode auth_hash; + + /** + * Salt used to hash the "Authentication" header, the result must then + * match the @e auth_hash. + */ + struct GNUNET_ShortHashCode auth_salt; +}; /** * General settings for an instance. @@ -118,17 +134,6 @@ struct TALER_MERCHANTDB_InstanceSettings */ struct GNUNET_TIME_Relative default_pay_delay; - /** - * Hash used for authentication. All zero if authentication is off. - */ - struct GNUNET_HashCode auth_hash; - - /** - * Salt used to hash the "Authentication" header, the result must then - * match the @e auth_hash. - */ - struct GNUNET_ShortHashCode auth_salt; - }; @@ -148,6 +153,7 @@ 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[]); @@ -784,7 +790,8 @@ struct TALER_MERCHANTDB_Plugin (*insert_instance)(void *cls, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPrivateKeyP *merchant_priv, - const struct TALER_MERCHANTDB_InstanceSettings *is); + const struct TALER_MERCHANTDB_InstanceSettings *is, + const struct TALER_MERCHANTDB_InstanceAuthSettings *ias); /** * Insert information about an instance's account into our database. @@ -836,6 +843,20 @@ struct TALER_MERCHANTDB_Plugin const struct TALER_MERCHANTDB_InstanceSettings *is); /** + * Update information about an instance's authentication settings + * into our database. + * + * @param cls closure + * @param merchant_id merchant backend instance ID + * @param ias instance auth settings + * @return database result code + */ + enum GNUNET_DB_QueryStatus + (*update_instance_auth)(void *cls, + const char *merchant_id, + const struct TALER_MERCHANTDB_InstanceAuthSettings *ias); + + /** * Set an instance's account in our database to "inactive". * * @param cls closure diff --git a/src/lib/merchant_api_patch_instance.c b/src/lib/merchant_api_patch_instance.c index 536fbdcf..bd69a8ba 100644 --- a/src/lib/merchant_api_patch_instance.c +++ b/src/lib/merchant_api_patch_instance.c @@ -169,7 +169,6 @@ TALER_MERCHANT_instance_patch ( const struct TALER_Amount *default_max_deposit_fee, struct GNUNET_TIME_Relative default_wire_transfer_delay, struct GNUNET_TIME_Relative default_pay_delay, - const char *auth_token, TALER_MERCHANT_InstancePatchCallback cb, void *cb_cls) { @@ -177,19 +176,6 @@ TALER_MERCHANT_instance_patch ( json_t *jpayto_uris; json_t *req_obj; - if (NULL != auth_token) - { - if (0 != strncasecmp (RFC_8959_PREFIX, - auth_token, - strlen (RFC_8959_PREFIX))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Authentication token must start with `%s'\n", - RFC_8959_PREFIX); - return NULL; - } - auth_token += strlen (RFC_8959_PREFIX); - } jpayto_uris = json_array (); if (NULL == jpayto_uris) { @@ -207,10 +193,8 @@ TALER_MERCHANT_instance_patch ( return NULL; } } - req_obj = json_pack ("{s:s?, s:o, s:s, s:O, s:O" + req_obj = json_pack ("{s:o, s:s, s:O, s:O" " s:o, s:I: s:o, s:o, s:o}", - "auth_token", - auth_token, "payto_uris", jpayto_uris, "name", diff --git a/src/lib/merchant_api_post_instances.c b/src/lib/merchant_api_post_instances.c index a6452045..8aede243 100644 --- a/src/lib/merchant_api_post_instances.c +++ b/src/lib/merchant_api_post_instances.c @@ -178,6 +178,7 @@ TALER_MERCHANT_instances_post ( struct TALER_MERCHANT_InstancesPostHandle *iph; json_t *jpayto_uris; json_t *req_obj; + json_t *auth_obj; if (NULL != auth_token) { @@ -190,11 +191,27 @@ TALER_MERCHANT_instances_post ( RFC_8959_PREFIX); return NULL; } - auth_token += strlen (RFC_8959_PREFIX); + auth_obj = json_pack ("{s:s, s:s}", + "method", + "token", + "token", + auth_token); + } + else + { + auth_obj = json_pack ("{s:s}", + "method", + "external"); + } + if (NULL == auth_obj) + { + GNUNET_break (0); + return NULL; } jpayto_uris = json_array (); if (NULL == jpayto_uris) { + json_decref (auth_obj); GNUNET_break (0); return NULL; } @@ -205,13 +222,14 @@ TALER_MERCHANT_instances_post ( json_string (payto_uris[i]))) { GNUNET_break (0); + json_decref (auth_obj); json_decref (jpayto_uris); return NULL; } } req_obj = json_pack ("{s:o, s:s, s:s, s:O, s:O" ",s:o, s:I: s:o, s:o, s:o" - ",s:s?}", + ",s:o}", "payto_uris", jpayto_uris, "id", @@ -233,10 +251,11 @@ TALER_MERCHANT_instances_post ( GNUNET_JSON_from_time_rel (default_wire_transfer_delay), "default_pay_delay", GNUNET_JSON_from_time_rel (default_pay_delay), - "auth_token", - auth_token); + "auth", + auth_obj); if (NULL == req_obj) { + json_decref (auth_obj); GNUNET_break (0); return NULL; } diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index fb045996..cad802fb 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -6,6 +6,9 @@ if USE_COVERAGE XLIB = -lgcov endif + +check_SCRIPTS = test-merchant-walletharness.sh + lib_LTLIBRARIES = \ libtalermerchanttesting.la @@ -82,7 +85,9 @@ endif endif TESTS = \ - $(check_PROGRAMS) + $(check_PROGRAMS) \ + $(check_SCRIPTS) + test_merchant_api_twisted_SOURCES = \ test_merchant_api_twisted.c diff --git a/src/testing/test-merchant-walletharness.sh b/src/testing/test-merchant-walletharness.sh new file mode 100755 index 00000000..f3f1d59a --- /dev/null +++ b/src/testing/test-merchant-walletharness.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# This script runs test from the wallet's integration test harness. +# If the wallet is not installed, the tests are skipped. +# Only tests from the "merchant" test suite are run. + +set -eu + +res=0 +taler-wallet-cli testing run-integrationtests --dry --suites merchant 2&>/dev/null || res=$? + +if [[ $res -ne 0 ]]; then + echo "skipping wallet test harness" + exit 77 +fi + + +exec taler-wallet-cli testing run-integrationtests --suites merchant diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c index b1932cd6..93b6194f 100644 --- a/src/testing/test_merchant_api.c +++ b/src/testing/test_merchant_api.c @@ -1181,7 +1181,6 @@ run (void *cls, "USD:0.5", GNUNET_TIME_UNIT_MINUTES, GNUNET_TIME_UNIT_MINUTES, - NULL, MHD_HTTP_CONFLICT), TALER_TESTING_cmd_merchant_patch_instance ("instance-patch-i1", merchant_url, @@ -1200,7 +1199,6 @@ run (void *cls, "EUR:0.5", GNUNET_TIME_UNIT_MINUTES, GNUNET_TIME_UNIT_MINUTES, - NULL, MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_get_instance2 ("instances-get-i1-2", merchant_url, @@ -1229,7 +1227,6 @@ run (void *cls, "EUR:0.5", GNUNET_TIME_UNIT_MINUTES, GNUNET_TIME_UNIT_MINUTES, - NULL, MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_get_instance2 ("instances-get-i1-3", merchant_url, @@ -1265,7 +1262,10 @@ run (void *cls, "EUR:0.2", GNUNET_TIME_UNIT_MINUTES, GNUNET_TIME_UNIT_MINUTES, - RFC_8959_PREFIX "EXAMPLE", + // FIXME: change this back once + // we have a update auth test CMD + //RFC_8959_PREFIX "EXAMPLE", + NULL, MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_patch_instance ("instance-patch-ACL", merchant_url, @@ -1284,7 +1284,6 @@ run (void *cls, "EUR:0.5", GNUNET_TIME_UNIT_MINUTES, GNUNET_TIME_UNIT_MINUTES, - RFC_8959_PREFIX "SANDBOX", MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2", merchant_url, diff --git a/src/testing/testing_api_cmd_patch_instance.c b/src/testing/testing_api_cmd_patch_instance.c index 2cbd0e5b..a7a82ef7 100644 --- a/src/testing/testing_api_cmd_patch_instance.c +++ b/src/testing/testing_api_cmd_patch_instance.c @@ -80,11 +80,6 @@ struct PatchInstanceState json_t *jurisdiction; /** - * Authentication token to require, can be NULL. - */ - const char *auth_token; - - /** * Wire fee to use. */ struct TALER_Amount default_max_wire_fee; @@ -183,7 +178,6 @@ patch_instance_run (void *cls, &pis->default_max_deposit_fee, pis->default_wire_transfer_delay, pis->default_pay_delay, - pis->auth_token, &patch_instance_cb, pis); GNUNET_assert (NULL != pis->iph); @@ -286,7 +280,6 @@ TALER_TESTING_cmd_merchant_patch_instance ( const char *default_max_deposit_fee, struct GNUNET_TIME_Relative default_wire_transfer_delay, struct GNUNET_TIME_Relative default_pay_delay, - const char *auth_token, unsigned int http_status) { struct PatchInstanceState *pis; @@ -313,7 +306,6 @@ TALER_TESTING_cmd_merchant_patch_instance ( &pis->default_max_deposit_fee)); pis->default_wire_transfer_delay = default_wire_transfer_delay; pis->default_pay_delay = default_pay_delay; - pis->auth_token = auth_token; { struct TALER_TESTING_Command cmd = { .cls = pis, |