commit 2b714201944476486e91965f63b4e2cc8b18b427
parent de3eab29eebec8d1c1b1bf0ea2adc39ba647691a
Author: Florian Dold <dold@taler.net>
Date: Fri, 11 Sep 2026 19:30:59 +0200
merchant auth: allow order-pos tokens to grant refunds
Issue: https://bugs.taler.net/n/11767
Diffstat:
3 files changed, 55 insertions(+), 3 deletions(-)
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
@@ -64,7 +64,7 @@ enum TMH_AuthScope
TMH_AS_ORDER_SIMPLE = 3,
/**
- * Order creation and inventory locking,
+ * Order creation, inventory locking and refunds,
* includes #TMH_AS_ORDER_SIMPLE
*/
TMH_AS_ORDER_POS = 4,
diff --git a/src/backend/taler-merchant-httpd_auth.c b/src/backend/taler-merchant-httpd_auth.c
@@ -93,11 +93,11 @@ static struct ScopePermissionMap scope_permissions[] = {
.name = "order-simple",
.permissions = "orders-read,orders-write"
},
- /* Simple order management for PoS, also allows inventory locking */
+ /* Order management for PoS, allows inventory locking and refunds */
{
.as = TMH_AS_ORDER_POS,
.name = "order-pos",
- .permissions = "orders-read,orders-write,pos-read,products-read,products-lock"
+ .permissions = "orders-read,orders-write,pos-read,products-read,products-lock,orders-refund"
},
/* Simple order management, also allows refunding */
{
diff --git a/src/testing/test_merchant_instance_auth.sh b/src/testing/test_merchant_instance_auth.sh
@@ -84,6 +84,58 @@ fi
echo " OK" >&2
+# Check both refund endpoints with scoped bearer tokens. An unknown order
+# must reach the refund handler (404/2005) only when the scope permits refunds.
+for SCOPE in readonly order-simple order-pos order-pos:refreshable order-mgmt order-full
+do
+ echo -n "Checking refund access for ${SCOPE} ..." >&2
+ STATUS=$(curl -H "Content-Type: application/json" \
+ -H "Authorization: Basic $BASIC_AUTH" \
+ http://localhost:9966/private/token \
+ -d "$(jq -n --arg scope "$SCOPE" '{scope: $scope}')" \
+ -w "%{http_code}" -s -o "$LAST_RESPONSE")
+ if [ "$STATUS" != "200" ]
+ then
+ cat "$LAST_RESPONSE" >&2
+ exit_fail "Expected 200, ${SCOPE} token created. Got: $STATUS"
+ fi
+ SCOPED_TOKEN=$(jq -e -r .access_token < "$LAST_RESPONSE")
+
+ case "$SCOPE" in
+ readonly|order-simple)
+ EXPECTED_STATUS=401
+ EXPECTED_CODE=2015 # TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED
+ ;;
+ *)
+ EXPECTED_STATUS=404
+ EXPECTED_CODE=2005 # TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN
+ ;;
+ esac
+ for ENDPOINT in refund refund-external
+ do
+ case "$ENDPOINT" in
+ refund)
+ REFUND_BODY='{"refund":"TESTKUDOS:1","reason":"scope test"}'
+ ;;
+ refund-external)
+ REFUND_BODY='{"method":"cash","id":"scope-test","amount":"TESTKUDOS:1","reason":"scope test"}'
+ ;;
+ esac
+ STATUS=$(curl -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $SCOPED_TOKEN" \
+ "http://localhost:9966/private/orders/unknown-order/${ENDPOINT}" \
+ -d "$REFUND_BODY" \
+ -w "%{http_code}" -s -o "$LAST_RESPONSE")
+ if [ "$STATUS" != "$EXPECTED_STATUS" ] ||
+ [ "$(jq -r .code < "$LAST_RESPONSE")" != "$EXPECTED_CODE" ]
+ then
+ cat "$LAST_RESPONSE" >&2
+ exit_fail "Expected ${EXPECTED_STATUS}/${EXPECTED_CODE} for ${SCOPE} on ${ENDPOINT}. Got: $STATUS"
+ fi
+ done
+ echo " OK" >&2
+done
+
# Kill merchant
kill -TERM "$SETUP_PID"
wait