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:
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);