commit 96c0fe1e71b2649c44f759d429579d2c830a6969
parent 0f77f90b48116c6509d1137b034724fc5f922c79
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Thu, 19 Jun 2025 10:22:26 +0200
cleanup and documentation
Diffstat:
3 files changed, 100 insertions(+), 32 deletions(-)
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -211,49 +211,105 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
*/
char *TMH_default_pass;
-#define THM_MAX_SCOPE_PERMISSIONS_LEN 4096
+/**
+ * Maximum length of a permissions string of a scope
+ */
+#define TMH_MAX_SCOPE_PERMISSIONS_LEN 4096
+/**
+ * Maximum length of a name of a scope
+ */
+#define TMH_MAX_NAME_LEN 255
+
+/**
+ * Represents a hard-coded set of default scopes with their
+ * permissions and names
+ */
struct ScopePermissionMap
{
+ /**
+ * The scope enum value
+ */
enum TMH_AuthScope as;
- char permissions[THM_MAX_SCOPE_PERMISSIONS_LEN];
+
+ /**
+ * The scope name
+ */
+ char name[TMH_MAX_NAME_LEN];
+
+ /**
+ * The scope permissions string.
+ * Comma-separated.
+ */
+ char permissions[TMH_MAX_SCOPE_PERMISSIONS_LEN];
};
+/**
+ * The default scopes array for merchant
+ */
struct ScopePermissionMap scope_permissions[] = {
+ /* Deprecated since v42 */
+ {
+ .as = TMH_AS_ADMIN,
+ .name = "write",
+ .permissions = "*"
+ },
+ /* Full access */
{
- .as = TMH_AS_ALL,
+ .as = TMH_AS_ADMIN,
+ .name = "admin",
.permissions = "*"
},
+ /* Read-only access */
{
.as = TMH_AS_READ_ONLY,
+ .name = "readonly",
.permissions = "*-read"
},
+ /* Simple order management */
{
.as = TMH_AS_ORDER_SIMPLE,
+ .name = "order-simple",
.permissions = "orders-read,orders-write"
},
+ /* Simple order management for PoS, also allows inventory locking */
{
.as = TMH_AS_ORDER_POS,
+ .name = "order-pos",
.permissions = "orders-read,orders-write,inventory-lock"
},
+ /* Simple order management, also allows refunding */
{
.as = TMH_AS_ORDER_MGMT,
+ .name = "order-mgmt",
.permissions = "orders-read,orders-write,orders-refund"
},
+ /* Full order management, allows inventory locking and refunds */
{
.as = TMH_AS_ORDER_FULL,
+ .name = "order-full",
.permissions = "orders-read,orders-write,inventory-lock,orders-refund"
},
+ /* No permissions, dummy scope */
{
.as = TMH_AS_NONE,
}
};
+/**
+ * Get permissions string for scope.
+ * Also extracts the leftmost bit into the @a refreshable
+ * output parameter.
+ *
+ * @param as the scope to get the permissions string from
+ * @param[out] refreshable true if the token associated with this scope is refreshable.
+ * @return the permissions string, or NULL if no such scope found
+ */
static const char*
get_scope_permissions (enum TMH_AuthScope as, bool *refreshable)
{
*refreshable = as & TMH_AS_REFRESHABLE;
- if (as != TMH_AS_ALL)
+ if (as != TMH_AS_ADMIN)
as &= ~TMH_AS_REFRESHABLE;
for (int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
{
@@ -264,9 +320,17 @@ get_scope_permissions (enum TMH_AuthScope as, bool *refreshable)
}
+/**
+ * Checks if @a permission_required is in permissions of
+ * @a scope.
+ *
+ * @param permission_required the permission to check.
+ * @param scope the scope to check.
+ * @return true if @a permission_required is in the permissions set of @a scope.
+ */
static bool
-capabilities_in_scope (const char*permission_required,
- enum TMH_AuthScope scope)
+permission_in_scope (const char *permission_required,
+ enum TMH_AuthScope scope)
{
char *permissions;
const char*perms_tmp;
@@ -360,7 +424,7 @@ TMH_scope_is_subset (enum TMH_AuthScope as, enum TMH_AuthScope candidate)
}
while (NULL != perm)
{
- if (! capabilities_in_scope (perm, as))
+ if (! permission_in_scope (perm, as))
{
GNUNET_free (permissions);
return false;
@@ -373,6 +437,18 @@ TMH_scope_is_subset (enum TMH_AuthScope as, enum TMH_AuthScope candidate)
}
+enum TMH_AuthScope
+TMH_get_scope_by_name (const char *name)
+{
+ for (int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
+ {
+ if (0 == strcasecmp (scope_permissions[i].name, name))
+ return scope_permissions[i].as;
+ }
+ return TMH_AS_NONE;
+}
+
+
enum GNUNET_GenericReturnValue
TMH_check_auth (const char *token,
struct TALER_MerchantAuthenticationSaltP *salt,
@@ -2161,7 +2237,7 @@ url_handler (void *cls,
if ( (0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) &&
(NULL == TMH_default_pass) )
{
- hc->auth_scope = TMH_AS_ALL;
+ hc->auth_scope = TMH_AS_ADMIN;
}
else if (is_basic_auth)
{
@@ -2188,7 +2264,7 @@ url_handler (void *cls,
if (GNUNET_OK ==
TMH_check_auth_instance (auth,
hc->instance))
- hc->auth_scope = TMH_AS_ALL;
+ hc->auth_scope = TMH_AS_ADMIN;
else
hc->auth_scope = TMH_AS_NONE;
}
@@ -2200,7 +2276,7 @@ url_handler (void *cls,
if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
{
/* hash zero means no authentication for instance */
- hc->auth_scope = TMH_AS_ALL;
+ hc->auth_scope = TMH_AS_ADMIN;
}
else
{
@@ -2229,7 +2305,7 @@ url_handler (void *cls,
}
else
{
- hc->auth_scope = TMH_AS_ALL;
+ hc->auth_scope = TMH_AS_ADMIN;
}
}
else
@@ -2249,7 +2325,7 @@ url_handler (void *cls,
- rh has an explicit non-NONE scope that matches
- scope is 'read only' and we have a GET request */
if ((NULL != hc->rh->permission) &&
- (! capabilities_in_scope (hc->rh->permission, hc->auth_scope)))
+ (! permission_in_scope (hc->rh->permission, hc->auth_scope)))
{
if (auth_malformed &&
(TMH_AS_NONE == hc->auth_scope) )
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
@@ -452,7 +452,7 @@ enum TMH_AuthScope
/**
* Full access is granted to everything.
*/
- TMH_AS_ALL = 7 | 1 << 31,
+ TMH_AS_ADMIN = 7 | 1 << 31,
/**
* /login access to renew the token is OK.
@@ -859,4 +859,13 @@ TMH_compute_auth (const char *token,
bool
TMH_scope_is_subset (enum TMH_AuthScope as, enum TMH_AuthScope candidate);
+/**
+ * Return the TMH_AuthScope corresponding to @a name.
+ *
+ * @param name the name to look for
+ * @return the scope corresponding to the name, or TMH_AS_NONE.
+ */
+enum TMH_AuthScope
+TMH_get_scope_by_name (const char *name);
+
#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
@@ -87,25 +87,8 @@ TMH_private_post_instances_ID_token (const struct TMH_RequestHandler *rh,
if ((NULL != scope_suffix) &&
(0 == strcasecmp (scope_suffix, "refreshable")))
refreshable = true;
- if (0 == strcasecmp (scope_prefix,
- "readonly"))
- iscope = TMH_AS_READ_ONLY;
- else if (0 == strcasecmp (scope_prefix,
- "write"))
- iscope = TMH_AS_ALL;
- else if (0 == strcasecmp (scope_prefix,
- "order-simple"))
- iscope = TMH_AS_ORDER_SIMPLE;
- else if (0 == strcasecmp (scope_prefix,
- "order-pos"))
- iscope = TMH_AS_ORDER_POS;
- else if (0 == strcasecmp (scope_prefix,
- "order-mgmt"))
- iscope = TMH_AS_ORDER_MGMT;
- else if (0 == strcasecmp (scope_prefix,
- "order-full"))
- iscope = TMH_AS_ORDER_FULL;
- else
+ iscope = TMH_get_scope_by_name (scope_prefix);
+ if (TMH_AS_NONE == iscope)
{
GNUNET_break_op (0);
GNUNET_free (tmp_scope);