commit 91f792719cf1d0b6e9f31d197596112e9df0adf6
parent 7f743fddc8e1a8f67aabaa99e55380f9c90aba06
Author: Florian Dold <dold@taler.net>
Date: Sat, 22 Aug 2026 20:21:57 +0200
sandcastle: keep subscription token families valid forever
Diffstat:
1 file changed, 77 insertions(+), 11 deletions(-)
diff --git a/scripts/demo/setup-sandcastle.sh b/scripts/demo/setup-sandcastle.sh
@@ -1236,16 +1236,77 @@ taler-harness deployment provision-merchant-instance \
# token families needed by demo blog
langs=(de en ar zh fr hi it ja ko pt pt_BR ru es sv tr uk)
-valid_before_ts=$(date -u +%s -d '+1 year') # one year later
duration_us=$((30 * 24 * 60 * 60 * 1000000)) # 30 days
validity_granularity_us=$((24 * 60 * 60 * 1000000)) # 1 day
+# valid_before is intentionally omitted so token families remain valid forever.
+
+provision_token_family() {
+ local instance_url=$1
+ local access_token=$2
+ local slug=$3
+ local request_body=$4
+ local response_file
+ local response_status
+ local update_body
+
+ response_file=$(mktemp)
+ response_status=$(curl -X POST "${instance_url}private/tokenfamilies" \
+ -H "Authorization: Bearer $access_token" \
+ -H "Content-Type: application/json" \
+ --data-raw "$request_body" \
+ --silent --show-error \
+ --output "$response_file" \
+ --write-out '%{http_code}')
+
+ case "$response_status" in
+ 204)
+ ;;
+ 409)
+ response_status=$(curl "${instance_url}private/tokenfamilies/${slug}" \
+ -H "Authorization: Bearer $access_token" \
+ --silent --show-error \
+ --output "$response_file" \
+ --write-out '%{http_code}')
+ if [[ $response_status != 200 ]]; then
+ echo "Failed to retrieve existing token family ${slug} (HTTP $response_status)" >&2
+ cat "$response_file" >&2
+ rm -f "$response_file"
+ return 1
+ fi
+
+ # PATCH only the mutable fields, preserving the original validity start.
+ # Omitting valid_before changes the validity end to "forever".
+ update_body=$(jq '
+ {name, description, description_i18n, valid_after}
+ + (if .extra_data == null then {} else {extra_data} end)
+ ' "$response_file")
+ response_status=$(curl -X PATCH "${instance_url}private/tokenfamilies/${slug}" \
+ -H "Authorization: Bearer $access_token" \
+ -H "Content-Type: application/json" \
+ --data-raw "$update_body" \
+ --silent --show-error \
+ --output "$response_file" \
+ --write-out '%{http_code}')
+ if [[ $response_status != 204 ]]; then
+ echo "Failed to update token family ${slug} (HTTP $response_status)" >&2
+ cat "$response_file" >&2
+ rm -f "$response_file"
+ return 1
+ fi
+ ;;
+ *)
+ echo "Failed to create token family ${slug} (HTTP $response_status)" >&2
+ cat "$response_file" >&2
+ rm -f "$response_file"
+ return 1
+ ;;
+ esac
+ rm -f "$response_file"
+}
# FIXME: Move this into a harness tool?
for lang in "${langs[@]}"; do
- curl -X POST "${MERCHANT_BASEURL}instances/blog/private/tokenfamilies" \
- -H "Authorization: Bearer secret-token:$(get_credential_pw merchant/blog)" \
- -H "Content-Type: application/json" \
- --data-raw "{
+ blog_token_family="{
\"kind\": \"subscription\",
\"slug\": \"blog_abo_${lang}\",
\"name\": \"One month of access (${lang})\",
@@ -1256,10 +1317,14 @@ for lang in "${langs[@]}"; do
\"fr\": \"Un mois d'accès aux articles\",
\"es\": \"Un mes de acceso a los artículos\"
},
- \"valid_before\": { \"t_s\": ${valid_before_ts} },
\"duration\": { \"d_us\": ${duration_us} },
\"validity_granularity\": { \"d_us\": ${validity_granularity_us} }
}"
+ provision_token_family \
+ "${MERCHANT_BASEURL}instances/blog/" \
+ "secret-token:$(get_credential_pw merchant/blog)" \
+ "blog_abo_${lang}" \
+ "$blog_token_family"
done
@@ -1272,18 +1337,19 @@ PAIVANA_TOKEN=secret-token:$(get_credential_pw merchant/paivana)
# Subscription offered as an alternative to paying per visit.
# Reuses the validity settings of the blog subscriptions above.
-curl -X POST "${PAIVANA_INSTANCE_URL}private/tokenfamilies" \
- -H "Authorization: Bearer $PAIVANA_TOKEN" \
- -H "Content-Type: application/json" \
- --data-raw "{
+paivana_token_family="{
\"kind\": \"subscription\",
\"slug\": \"paivana_abo\",
\"name\": \"One month of access\",
\"description\": \"One month of access to $PAIVANA_DESTINATION\",
- \"valid_before\": { \"t_s\": ${valid_before_ts} },
\"duration\": { \"d_us\": ${duration_us} },
\"validity_granularity\": { \"d_us\": ${validity_granularity_us} }
}"
+provision_token_family \
+ "$PAIVANA_INSTANCE_URL" \
+ "$PAIVANA_TOKEN" \
+ paivana_abo \
+ "$paivana_token_family"
# paivana-httpd learns which URLs cost how much from the templates
# of its merchant instance. A single template covering the whole