merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 0f77f90b48116c6509d1137b034724fc5f922c79
parent 0a27ab1fd54e3ad0465975eda0322c82f3843cf2
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Wed, 18 Jun 2025 21:42:30 +0200

rework scope handling again with decent backwards compat

Diffstat:
Msrc/backend/taler-merchant-httpd.c | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/backend/taler-merchant-httpd.h | 82+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/backend/taler-merchant-httpd_private-post-instances-ID-token.c | 5++---
3 files changed, 276 insertions(+), 72 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c @@ -153,6 +153,12 @@ static struct GNUNET_DB_EventHandler *instance_eh; struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map; /** + * GNUNET_YES if protocol version 42 is strictly enforced. + * (Default is GNUNET_NO) + */ +int TMH_strict_v42; + +/** * How long do we need to keep information on paid contracts on file for tax * or other legal reasons? Used to block deletions for younger transaction * data. @@ -205,16 +211,165 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg; */ char *TMH_default_pass; +#define THM_MAX_SCOPE_PERMISSIONS_LEN 4096 + +struct ScopePermissionMap +{ + enum TMH_AuthScope as; + char permissions[THM_MAX_SCOPE_PERMISSIONS_LEN]; +}; + +struct ScopePermissionMap scope_permissions[] = { + { + .as = TMH_AS_ALL, + .permissions = "*" + }, + { + .as = TMH_AS_READ_ONLY, + .permissions = "*-read" + }, + { + .as = TMH_AS_ORDER_SIMPLE, + .permissions = "orders-read,orders-write" + }, + { + .as = TMH_AS_ORDER_POS, + .permissions = "orders-read,orders-write,inventory-lock" + }, + { + .as = TMH_AS_ORDER_MGMT, + .permissions = "orders-read,orders-write,orders-refund" + }, + { + .as = TMH_AS_ORDER_FULL, + .permissions = "orders-read,orders-write,inventory-lock,orders-refund" + }, + { + .as = TMH_AS_NONE, + } +}; + +static const char* +get_scope_permissions (enum TMH_AuthScope as, bool *refreshable) +{ + *refreshable = as & TMH_AS_REFRESHABLE; + if (as != TMH_AS_ALL) + as &= ~TMH_AS_REFRESHABLE; + for (int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++) + { + if (as == scope_permissions[i].as) + return scope_permissions[i].permissions; + } + return NULL; +} + + static bool -capabilities_in_scope (enum TMH_AuthScope scope, enum TMH_AuthCapability - required_capas) +capabilities_in_scope (const char*permission_required, + enum TMH_AuthScope scope) { - uint64_t diff_capas; + char *permissions; + const char*perms_tmp; + bool is_read_perm; + bool is_write_perm; + bool refreshable; + + perms_tmp = get_scope_permissions (scope, &refreshable); + if (NULL == perms_tmp) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Got scope: %u\n", scope); + GNUNET_break_op (0); + return false; + } + + char *last_dash = strrchr (permission_required, '-'); + if (NULL != last_dash) + { + is_write_perm = (0 == strcmp (last_dash, "-write")); + is_read_perm = (0 == strcmp (last_dash, "-read")); + } - /* Get capas that differ between required and scoped */ - diff_capas = scope ^ required_capas; - /* Check if differing capas are all covered by scoped capabilities */ - return diff_capas & scope; + if (refreshable && 0 == strcmp ("token-refresh", permission_required)) + return true; + permissions = GNUNET_strdup (perms_tmp); + { + char *perm = strtok (permissions, ","); + if (NULL == perm) + { + GNUNET_free (permissions); + return false; + } + while (NULL != perm) + { + if (0 == strcmp ("*", perm)) + { + GNUNET_free (permissions); + return true; + } + if ((0 == strcmp ("*-write", perm)) && + (is_write_perm)) + { + GNUNET_free (permissions); + return true; + } + if ((0 == strcmp ("*-read", perm)) && + (is_read_perm)) + { + GNUNET_free (permissions); + return true; + } + if (0 == strcmp (permission_required, perm)) + { + GNUNET_free (permissions); + return true; + } + perm = strtok (NULL, ","); + } + } + GNUNET_free (permissions); + return false; +} + + +bool +TMH_scope_is_subset (enum TMH_AuthScope as, enum TMH_AuthScope candidate) +{ + const char*as_perms; + const char*candidate_perms; + char *permissions; + bool as_refreshable; + bool cand_refreshable; + + as_perms = get_scope_permissions (as, &as_refreshable); + candidate_perms = get_scope_permissions (candidate, &cand_refreshable); + if (! as_refreshable && cand_refreshable) + return false; + if ((NULL == as_perms) && (NULL != candidate_perms)) + return false; + if ((NULL == candidate_perms) || + (0 == strcmp ("*", as_perms))) + return true; + permissions = GNUNET_strdup (candidate_perms); + { + char *perm = strtok (permissions, ","); + if (NULL == perm) + { + GNUNET_free (permissions); + return true; + } + while (NULL != perm) + { + if (! capabilities_in_scope (perm, as)) + { + GNUNET_free (permissions); + return false; + } + perm = strtok (NULL, ","); + } + } + GNUNET_free (permissions); + return true; } @@ -832,6 +987,7 @@ url_handler (void *cls, { .url_prefix = "/instances", .method = MHD_HTTP_METHOD_GET, + .permission = "instances-write", .skip_instance = true, .default_only = true, .handler = &TMH_private_get_instances @@ -840,6 +996,7 @@ url_handler (void *cls, { .url_prefix = "/instances", .method = MHD_HTTP_METHOD_POST, + .permission = "instances-write", .skip_instance = true, .default_only = true, .handler = &TMH_private_post_instances, @@ -853,6 +1010,7 @@ url_handler (void *cls, { .url_prefix = "/instances/", .method = MHD_HTTP_METHOD_GET, + .permission = "instances-write", .skip_instance = true, .default_only = true, .have_id_segment = true, @@ -862,6 +1020,7 @@ url_handler (void *cls, { .url_prefix = "/instances/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "instances-write", .skip_instance = true, .default_only = true, .have_id_segment = true, @@ -871,6 +1030,7 @@ url_handler (void *cls, { .url_prefix = "/instances/", .method = MHD_HTTP_METHOD_PATCH, + .permission = "instances-write", .skip_instance = true, .default_only = true, .have_id_segment = true, @@ -886,6 +1046,7 @@ url_handler (void *cls, .url_prefix = "/instances/", .url_suffix = "auth", .method = MHD_HTTP_METHOD_POST, + .permission = "instances-auth-write", .skip_instance = true, .default_only = true, .have_id_segment = true, @@ -898,6 +1059,7 @@ url_handler (void *cls, .url_prefix = "/instances/", .url_suffix = "kyc", .method = MHD_HTTP_METHOD_GET, + .permission = "instances-kyc-read", .skip_instance = true, .default_only = true, .have_id_segment = true, @@ -913,12 +1075,14 @@ url_handler (void *cls, { .url_prefix = "/", .method = MHD_HTTP_METHOD_GET, + .permission = "instances-read", .handler = &TMH_private_get_instances_ID }, /* DELETE /instances/$ID/: */ { .url_prefix = "/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "instances-write", .allow_deleted_instance = true, .handler = &TMH_private_delete_instances_ID }, @@ -927,6 +1091,7 @@ url_handler (void *cls, .url_prefix = "/", .method = MHD_HTTP_METHOD_PATCH, .handler = &TMH_private_patch_instances_ID, + .permission = "instances-write", .allow_deleted_instance = true, /* allow instance data of up to 8 MB, that should be plenty; note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) @@ -939,6 +1104,7 @@ url_handler (void *cls, .url_prefix = "/auth", .method = MHD_HTTP_METHOD_POST, .handler = &TMH_private_post_instances_ID_auth, + .permission = "auth-write", /* Body should be pretty small. */ .max_upload = 1024 * 1024, }, @@ -946,24 +1112,28 @@ url_handler (void *cls, { .url_prefix = "/kyc", .method = MHD_HTTP_METHOD_GET, + .permission = "kyc-read", .handler = &TMH_private_get_instances_ID_kyc, }, /* GET /pos: */ { .url_prefix = "/pos", .method = MHD_HTTP_METHOD_GET, + .permission = "pos-read", .handler = &TMH_private_get_pos }, /* GET /categories: */ { .url_prefix = "/categories", .method = MHD_HTTP_METHOD_GET, + .permission = "categories-read", .handler = &TMH_private_get_categories }, /* POST /categories: */ { .url_prefix = "/categories", .method = MHD_HTTP_METHOD_POST, + .permission = "categories-write", .handler = &TMH_private_post_categories, /* allow category data of up to 8 kb, that should be plenty */ .max_upload = 1024 * 8 @@ -972,6 +1142,7 @@ url_handler (void *cls, { .url_prefix = "/categories/", .method = MHD_HTTP_METHOD_GET, + .permission = "categories-read", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_get_categories_ID @@ -980,6 +1151,7 @@ url_handler (void *cls, { .url_prefix = "/categories/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "categories-write", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_delete_categories_ID @@ -988,6 +1160,7 @@ url_handler (void *cls, { .url_prefix = "/categories/", .method = MHD_HTTP_METHOD_PATCH, + .permission = "categories-write", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_patch_categories_ID, @@ -997,6 +1170,7 @@ url_handler (void *cls, /* GET /products: */ { .url_prefix = "/products", + .permission = "products-read", .method = MHD_HTTP_METHOD_GET, .handler = &TMH_private_get_products }, @@ -1004,6 +1178,7 @@ url_handler (void *cls, { .url_prefix = "/products", .method = MHD_HTTP_METHOD_POST, + .permission = "products-write", .handler = &TMH_private_post_products, /* allow product data of up to 8 MB, that should be plenty; note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) @@ -1016,6 +1191,7 @@ url_handler (void *cls, .url_prefix = "/products/", .method = MHD_HTTP_METHOD_GET, .have_id_segment = true, + .permission = "products-read", .allow_deleted_instance = true, .handler = &TMH_private_get_products_ID }, @@ -1024,6 +1200,7 @@ url_handler (void *cls, .url_prefix = "/products/", .method = MHD_HTTP_METHOD_DELETE, .have_id_segment = true, + .permission = "products-write", .allow_deleted_instance = true, .handler = &TMH_private_delete_products_ID }, @@ -1033,6 +1210,7 @@ url_handler (void *cls, .method = MHD_HTTP_METHOD_PATCH, .have_id_segment = true, .allow_deleted_instance = true, + .permission = "products-write", .handler = &TMH_private_patch_products_ID, /* allow product data of up to 8 MB, that should be plenty; note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) @@ -1046,6 +1224,7 @@ url_handler (void *cls, .url_suffix = "lock", .method = MHD_HTTP_METHOD_POST, .have_id_segment = true, + .permission = "products-lock", .handler = &TMH_private_post_products_ID_lock, /* the body should be pretty small, allow 1 MB of upload to set a conservative bound for sane wallets */ @@ -1055,6 +1234,7 @@ url_handler (void *cls, { .url_prefix = "/orders", .method = MHD_HTTP_METHOD_POST, + .permission = "orders-write", .handler = &TMH_private_post_orders, /* allow contracts of up to 8 MB, that should be plenty; note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) @@ -1066,6 +1246,7 @@ url_handler (void *cls, { .url_prefix = "/orders/", .method = MHD_HTTP_METHOD_GET, + .permission = "orders-read", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_get_orders_ID @@ -1074,6 +1255,16 @@ url_handler (void *cls, { .url_prefix = "/orders", .method = MHD_HTTP_METHOD_GET, + // FIXME: What is the abstraction-level for permissions? + // We could have per endpoint permissions, and scopes as + // collections of permissions. + // But this will not allow fine-grained access control on the + // semantics of the request! Is that required? + // If not, each endpoint should have a permission string + // The permission string(s) associated with a role/scope can + // be defined in the config file. + // E.g. order-simple = orders-get; order-pos = orders-get,orders-post + .permission = "orders-read", .allow_deleted_instance = true, .handler = &TMH_private_get_orders }, @@ -1083,6 +1274,7 @@ url_handler (void *cls, .url_suffix = "refund", .method = MHD_HTTP_METHOD_POST, .have_id_segment = true, + .permission = "orders-refund", .handler = &TMH_private_post_orders_ID_refund, /* the body should be pretty small, allow 1 MB of upload to set a conservative bound for sane wallets */ @@ -1093,6 +1285,7 @@ url_handler (void *cls, .url_prefix = "/orders/", .url_suffix = "forget", .method = MHD_HTTP_METHOD_PATCH, + .permission = "orders-write", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_patch_orders_ID_forget, @@ -1104,6 +1297,7 @@ url_handler (void *cls, { .url_prefix = "/orders/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "orders-write", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_delete_orders_ID @@ -1114,6 +1308,7 @@ url_handler (void *cls, .method = MHD_HTTP_METHOD_POST, .allow_deleted_instance = true, .handler = &TMH_private_post_transfers, + .permission = "transfers-write", /* the body should be pretty small, allow 1 MB of upload to set a conservative bound for sane wallets */ .max_upload = 1024 * 1024 @@ -1122,6 +1317,7 @@ url_handler (void *cls, { .url_prefix = "/transfers/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "transfers-write", .allow_deleted_instance = true, .handler = &TMH_private_delete_transfers_ID, .have_id_segment = true, @@ -1132,6 +1328,7 @@ url_handler (void *cls, /* GET /transfers: */ { .url_prefix = "/transfers", + .permission = "transfers-read", .method = MHD_HTTP_METHOD_GET, .allow_deleted_instance = true, .handler = &TMH_private_get_transfers @@ -1139,12 +1336,14 @@ url_handler (void *cls, /* POST /otp-devices: */ { .url_prefix = "/otp-devices", + .permission = "otp-devices-write", .method = MHD_HTTP_METHOD_POST, .handler = &TMH_private_post_otp_devices }, /* GET /otp-devices: */ { .url_prefix = "/otp-devices", + .permission = "opt-devices-read", .method = MHD_HTTP_METHOD_GET, .handler = &TMH_private_get_otp_devices }, @@ -1152,6 +1351,7 @@ url_handler (void *cls, { .url_prefix = "/otp-devices/", .method = MHD_HTTP_METHOD_GET, + .permission = "otp-devices-read", .have_id_segment = true, .handler = &TMH_private_get_otp_devices_ID }, @@ -1159,6 +1359,7 @@ url_handler (void *cls, { .url_prefix = "/otp-devices/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "otp-devices-write", .have_id_segment = true, .handler = &TMH_private_delete_otp_devices_ID }, @@ -1166,6 +1367,7 @@ url_handler (void *cls, { .url_prefix = "/otp-devices/", .method = MHD_HTTP_METHOD_PATCH, + .permission = "otp-devices-write", .have_id_segment = true, .handler = &TMH_private_patch_otp_devices_ID }, @@ -1173,6 +1375,7 @@ url_handler (void *cls, { .url_prefix = "/templates", .method = MHD_HTTP_METHOD_POST, + .permission = "templates-write", .handler = &TMH_private_post_templates, /* allow template data of up to 8 MB, that should be plenty; note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) @@ -1183,6 +1386,7 @@ url_handler (void *cls, /* GET /templates: */ { .url_prefix = "/templates", + .permission = "templates-read", .method = MHD_HTTP_METHOD_GET, .handler = &TMH_private_get_templates }, @@ -1190,6 +1394,7 @@ url_handler (void *cls, { .url_prefix = "/templates/", .method = MHD_HTTP_METHOD_GET, + .permission = "templates-read", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_get_templates_ID @@ -1198,6 +1403,7 @@ url_handler (void *cls, { .url_prefix = "/templates/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "templates-write", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_delete_templates_ID @@ -1206,6 +1412,7 @@ url_handler (void *cls, { .url_prefix = "/templates/", .method = MHD_HTTP_METHOD_PATCH, + .permission = "templates-write", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_patch_templates_ID, @@ -1218,6 +1425,7 @@ url_handler (void *cls, /* GET /webhooks: */ { .url_prefix = "/webhooks", + .permission = "webhooks-read", .method = MHD_HTTP_METHOD_GET, .handler = &TMH_private_get_webhooks }, @@ -1225,6 +1433,7 @@ url_handler (void *cls, { .url_prefix = "/webhooks", .method = MHD_HTTP_METHOD_POST, + .permission = "webhooks-write", .handler = &TMH_private_post_webhooks, /* allow webhook data of up to 8 MB, that should be plenty; note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) @@ -1236,6 +1445,7 @@ url_handler (void *cls, { .url_prefix = "/webhooks/", .method = MHD_HTTP_METHOD_GET, + .permission = "webhooks-read", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_get_webhooks_ID @@ -1243,6 +1453,7 @@ url_handler (void *cls, /* DELETE /webhooks/$ID/: */ { .url_prefix = "/webhooks/", + .permission = "webhooks-write", .method = MHD_HTTP_METHOD_DELETE, .have_id_segment = true, .allow_deleted_instance = true, @@ -1252,6 +1463,7 @@ url_handler (void *cls, { .url_prefix = "/webhooks/", .method = MHD_HTTP_METHOD_PATCH, + .permission = "webhooks-write", .have_id_segment = true, .allow_deleted_instance = true, .handler = &TMH_private_patch_webhooks_ID, @@ -1265,6 +1477,7 @@ url_handler (void *cls, { .url_prefix = "/accounts", .method = MHD_HTTP_METHOD_POST, + .permission = "accounts-write", .handler = &TMH_private_post_account, /* allow account details of up to 8 kb, that should be plenty */ .max_upload = 1024 * 8 @@ -1273,6 +1486,7 @@ url_handler (void *cls, { .url_prefix = "/accounts/", .method = MHD_HTTP_METHOD_PATCH, + .permission = "accounts-write", .handler = &TMH_private_patch_accounts_ID, .have_id_segment = true, /* allow account details of up to 8 kb, that should be plenty */ @@ -1281,12 +1495,14 @@ url_handler (void *cls, /* GET /accounts: */ { .url_prefix = "/accounts", + .permission = "accounts-read", .method = MHD_HTTP_METHOD_GET, .handler = &TMH_private_get_accounts }, /* GET /accounts/$H_WIRE: */ { .url_prefix = "/accounts/", + .permission = "accounts-read", .method = MHD_HTTP_METHOD_GET, .have_id_segment = true, .handler = &TMH_private_get_accounts_ID @@ -1294,6 +1510,7 @@ url_handler (void *cls, /* DELETE /accounts/$H_WIRE: */ { .url_prefix = "/accounts/", + .permission = "accounts-write", .method = MHD_HTTP_METHOD_DELETE, .handler = &TMH_private_delete_account_ID, .have_id_segment = true @@ -1301,7 +1518,7 @@ url_handler (void *cls, /* POST /token: */ { .url_prefix = "/token", - .ac_required = TMH_AC_REFRESHABLE, + .permission = "token-refresh", .method = MHD_HTTP_METHOD_POST, .handler = &TMH_private_post_instances_ID_token, /* Body should be tiny. */ @@ -1310,19 +1527,20 @@ url_handler (void *cls, /* DELETE /token: */ { .url_prefix = "/token", - .ac_required = TMH_AC_ALWAYS_READ, .method = MHD_HTTP_METHOD_DELETE, .handler = &TMH_private_delete_instances_ID_token, }, /* GET /tokenfamilies: */ { .url_prefix = "/tokenfamilies", + .permission = "tokenfamilies-read", .method = MHD_HTTP_METHOD_GET, .handler = &TMH_private_get_tokenfamilies }, /* POST /tokenfamilies: */ { .url_prefix = "/tokenfamilies", + .permission = "tokenfamilies-write", .method = MHD_HTTP_METHOD_POST, .handler = &TMH_private_post_token_families }, @@ -1330,6 +1548,7 @@ url_handler (void *cls, { .url_prefix = "/tokenfamilies/", .method = MHD_HTTP_METHOD_GET, + .permission = "tokenfamilies-read", .have_id_segment = true, .handler = &TMH_private_get_tokenfamilies_SLUG }, @@ -1337,6 +1556,7 @@ url_handler (void *cls, { .url_prefix = "/tokenfamilies/", .method = MHD_HTTP_METHOD_DELETE, + .permission = "tokenfamilies-write", .have_id_segment = true, .handler = &TMH_private_delete_token_families_SLUG }, @@ -1344,6 +1564,7 @@ url_handler (void *cls, { .url_prefix = "/tokenfamilies/", .method = MHD_HTTP_METHOD_PATCH, + .permission = "tokenfamilies-write", .have_id_segment = true, .handler = &TMH_private_patch_token_family_SLUG, }, @@ -1351,6 +1572,7 @@ url_handler (void *cls, { .url_prefix = "/statistics-counter/", .method = MHD_HTTP_METHOD_GET, + .permission = "statistics-read", .have_id_segment = true, .handler = &TMH_private_get_statistics_counter_SLUG, }, @@ -1358,6 +1580,7 @@ url_handler (void *cls, { .url_prefix = "/statistics-amount/", .method = MHD_HTTP_METHOD_GET, + .permission = "statistics-read", .have_id_segment = true, .handler = &TMH_private_get_statistics_amount_SLUG, }, @@ -1968,8 +2191,6 @@ url_handler (void *cls, hc->auth_scope = TMH_AS_ALL; else hc->auth_scope = TMH_AS_NONE; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Token auth scope %u\n", hc-> - auth_scope); } } else /* Check bearer token */ @@ -2027,12 +2248,8 @@ url_handler (void *cls, - scope is 'all' - rh has an explicit non-NONE scope that matches - scope is 'read only' and we have a GET request */ - if (! ( (TMH_AS_ALL == hc->auth_scope) || - ( (TMH_AC_NONE != hc->rh->ac_required) && - (capabilities_in_scope (hc->auth_scope, hc->rh->ac_required)) ) || - ( (TMH_AS_READ_ONLY == (hc->auth_scope & TMH_AS_READ_ONLY)) && - (0 == strcmp (MHD_HTTP_METHOD_GET, - method)) ) ) ) + if ((NULL != hc->rh->permission) && + (! capabilities_in_scope (hc->rh->permission, hc->auth_scope))) { if (auth_malformed && (TMH_AS_NONE == hc->auth_scope) ) @@ -2390,6 +2607,16 @@ run (void *cls, return; } + if (GNUNET_SYSERR != + (TMH_strict_v42 = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "merchant", + "STRICT_PROTOCOL_V42"))) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "merchant", + "STRICT_PROTOCOL_V42"); + TMH_strict_v42 = GNUNET_NO; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, "merchant", diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h @@ -408,49 +408,6 @@ struct TMH_HandlerContext; /** - * Access capabilities. This is a bit mask. - */ -enum TMH_AuthCapability -{ - /** - * Nothing is allowed. - */ - TMH_AC_NONE = 0, - - /** - * Token renewal is OK. - */ - TMH_AC_REFRESHABLE = 1, - - /** - * Read-only access is OK. Any GET request is - * automatically OK. - */ - TMH_AC_ALWAYS_READ = 2, - - /** - * Order creation and payment status check only - */ - TMH_AC_ORDER_CREATION = 4, - - /** - * Payment status check only - */ - TMH_AC_PAYMENT_STATUS = 8, - - /** - * Inventory locking, - */ - TMH_AC_INVENTORY_LOCKING = 16, - - /** - * Order creation and refund - */ - TMH_AC_REFUND = 32, - -}; - -/** * Possible authorization scopes. This is a bit mask. */ enum TMH_AuthScope @@ -458,40 +415,51 @@ enum TMH_AuthScope /** * Nothing is authorized. */ - TMH_AS_NONE = TMH_AC_NONE, + TMH_AS_NONE = 0, /** * Read-only access is OK. Any GET request is * automatically OK. */ - TMH_AS_READ_ONLY = TMH_AC_ALWAYS_READ, + TMH_AS_READ_ONLY = 1, + + /** + * 2 is Reserved. Was refreshable pre v42 + */ /** * Order creation and payment status check only */ - TMH_AS_ORDER_SIMPLE = TMH_AC_ORDER_CREATION | TMH_AC_PAYMENT_STATUS, + TMH_AS_ORDER_SIMPLE = 3, /** * Order creation and inventory locking, * includes #TMH_AS_ORDER_SIMPLE */ - TMH_AS_ORDER_POS = TMH_AS_ORDER_SIMPLE | TMH_AC_INVENTORY_LOCKING, + TMH_AS_ORDER_POS = 4, /** * Order creation and refund */ - TMH_AS_ORDER_MGMT = TMH_AS_ORDER_SIMPLE | TMH_AC_REFUND, + TMH_AS_ORDER_MGMT = 5, /** * Order full * Includes #TMH_ORDER_POS and #TMH_ORDER_MGMT */ - TMH_AS_ORDER_FULL = TMH_AS_ORDER_POS | TMH_AS_ORDER_MGMT, + TMH_AS_ORDER_FULL = 6, /** * Full access is granted to everything. */ - TMH_AS_ALL = UINT32_MAX, + TMH_AS_ALL = 7 | 1 << 31, + + /** + * /login access to renew the token is OK. + * This is actually combined with other scopes + * and not (usually) used as a scope itself. + */ + TMH_AS_REFRESHABLE = 1 << 31, }; @@ -517,9 +485,9 @@ struct TMH_RequestHandler const char *url_prefix; /** - * Required access capabilities for this request. + * Required access permission for this request. */ - enum TMH_AuthCapability ac_required; + const char *permission; /** * Does this request include an identifier segment @@ -881,4 +849,14 @@ TMH_compute_auth (const char *token, struct TALER_MerchantAuthenticationHashP *hash); +/** + * Check if @a candidate permissions are a subset of @a as permissions + * + * @param as scope to check against + * @param candidate scope to check if its permissions are a subset of @a as permissions. + * @return true if it was a subset, false otherwise. + */ +bool +TMH_scope_is_subset (enum TMH_AuthScope as, enum TMH_AuthScope candidate); + #endif diff --git a/src/backend/taler-merchant-httpd_private-post-instances-ID-token.c b/src/backend/taler-merchant-httpd_private-post-instances-ID-token.c @@ -115,9 +115,8 @@ TMH_private_post_instances_ID_token (const struct TMH_RequestHandler *rh, } } if (refreshable) - iscope |= TMH_AC_REFRESHABLE; - if ((TMH_AS_ALL != hc->auth_scope) && - (0 != (iscope & (~hc->auth_scope)))) + iscope |= TMH_AS_REFRESHABLE; + if (! TMH_scope_is_subset (hc->auth_scope, iscope)) { /* more permissions requested for the new token, not allowed */ GNUNET_break_op (0);