Skip to content

Webhook Subscriptions Modernized

TestLink CI edited this page Oct 20, 2026 · 1 revision

Webhook Subscriptions manager — Modernized (TestLink 2.0.1, Refs #1946, parent #1072)

Net-new, project-scoped outbound webhook subscriptions registry for TestLink 2.0.1, added as a System ASIDE entry. This is the third delivery slice of parent feature #1072 after the REST API Reference hub (#1937) and the OpenAPI Specification viewer (#1944): a registry of endpoints that TestLink can notify, with a real signed test delivery. Tracking issue #1946.

TestLink 1.9.20 and the initial 2.0.1 BFF layer shipped no webhook entity at all, so this is net-new — nothing was dropped from legacy.

Webhook Subscriptions — normal state

Why this screen

  • Parent enhancement #1072 asks for documented API access: a reference hub, a machine-readable spec and an outbound event mechanism. The first two landed (#1937, #1944); the delivery half was still missing.
  • The ledger's TODO section is empty (every ASIDE entry already maps to a modern .html + BFF), so a genuine net-new System screen was taken instead of a legacy port.
  • Integrators (Jenkins, GitLab CI, chat bots, incident tooling) need a first-class, per-project place to declare endpoints and prove they are reachable before relying on them in a pipeline.

Modern stack

Layer File
Screen gui/templates/system/webhooks.html (Dashio standalone HTML/JS/CSS)
BFF api/webhooks/index.php (GET|HEAD ?action=init|list, POST ?action=save|delete|test; session auth + same-origin guard + session-inactivity check)
Wiring $actions->webhooks in lib/functions/common.php, consumed by ASIDE System section (view_tc), plus api/aside/index.php
DB tc_webhooks (installer DDL + lazy CREATE TABLE IF NOT EXISTS, whitelisted in lib/functions/object.class.php)
i18n whk.* (78 keys, incl. whk.noMatch) in all 10 client bundles; $TLS_title_webhooks in all 19 server locales

Content

  • Metric tiles — subscriptions, active, inactive, test OK, test failed, never tested.
  • Filters — status (all / active / inactive) and event topic, plus a translated no subscription matches the current filters state.
  • Registry DataTable — Name / Endpoint URL / Topic / Status / Secret (Set or None — the raw secret is never echoed back) / Last test / Updated / Actions.
  • Create / edit modal — name, endpoint URL (http/https, credentials refused), event topic, active flag and an optional HMAC secret. On edit an empty secret keeps the current one.
  • Details modal — read-only summary including how deliveries are signed.
  • Delete confirmation — interpolates the subscription name.
  • Test delivery — POSTs a signed JSON payload and records HTTP status, duration and OK/failed into the row and tiles.

Behaviour & safety

  • Rights — reads require mgt_view_tc or mgt_modify_tc; writes require mgt_modify_tc, checked on the addressed project before it is resolved (the #1697 "no existence oracle" lesson). A foreign-project id returns the same 404 as a nonexistent one.
  • Project proof on writes — a bogus tproject_id cannot insert an orphan row (404 tproject_not_found).
  • Verb gate before rights — a wrong HTTP method answers 405, never 403.
  • SSRF guard — the test-delivery POST refuses loopback, link-local and cloud metadata targets (127.0.0.0/8, 169.254.0.0/16, 0.0.0.0/8, ::1, fe80::); RFC1918 LAN targets stay allowed on purpose (Jenkins/GitLab runners), and $tlCfg->webhooksAllowLocalTargets opts loopback back in. cURL protocols are pinned to HTTP/HTTPS.
  • Signing — every delivery carries X-TestLink-Event and, when a secret is set, X-TestLink-Signature: sha256=<hex HMAC-SHA256 of the raw body>.
  • Stable error codes — 400 no_testproject / name_required / bad_url / bad_topic / unknown_action, 401 not_authenticated, 403 no_right, 404 tproject_not_found / webhook_not_found, 405 wrong_method, 409 duplicate_name, 500 server_error, 502 delivery_failed.

Testing

  • Suite Issue #1946 in tmp/TLU_Test_Cases.md — 15/15 PASS (BFF contract + browser flows), Event Viewer AUDIT-only.
  • BFF contract re-verified on the fix pass: loopback/metadata URL → 400, LAN host → 200, bogus project → 404, PUT → 405, anon → 401, role-3 → 403, duplicate → 409, JSON null secret keeps the stored secret.
  • Browser (admin + role-3 whknorights): create / edit / details / test delivery / delete, filters + no-match, Romanian locale, 403 state card, ASIDE entry visible for admin and hidden for role 3, console clean (0 issues).

Create / edit modal

Details modal

Changelog

  • Refs #1946, parent #1072 — net-new Webhook Subscriptions manager.

Clone this wiki locally