Task Issue 960 issuetracker management Write Gating - sebiboga/testlink-upgraded GitHub Wiki

Task 960 — Issue Tracker write gating: issuetracker_management (create/edit/delete)

Issue: #960 Status: IMPLEMENTED & VERIFIED (2026-09-23) — branch task/issue-960

Legacy 1.9.20 clearly separates read from manage on Issue Trackers: lib/issuetrackers/issueTrackerView.php:24 set $gui->canManage = hasRight('issuetracker_management') and the whole write UI (issueTrackerView.tpl) — Create button (:95-100), the delete column + icon (:45,78-84), the wrench/connection icons and the name link (:54-73) — is rendered only for managers. A view-only user sees a bare table with plain-text names. The write controller issueTrackerEdit.php also requires issuetracker_management.

The modern screen exposed two privilege-escalation paths:

  1. API — POST /, PUT /{id}, DELETE /{id} and POST /oauth/create accepted requests from any user that passed the #959 read gate. Measured: itview (view-only) created + deleted a tracker (both HTTP 200).
  2. UI — the toolbar (+ Create Issue Tracker, GitHub) and the per-row Edit/Delete icons were rendered for itview exactly as for admin.

Server-side fix (api/issuetracker/index.php)

A $canManage boolean (cast from hasRight(issuetracker_management) — the method returns null, not false, for an absent right) plus a $writeDenied() closure that trails audit_security_user_right_missing (activity EDIT, object issuetrackers) into the Event Viewer before answering HTTP 403. Each write route begins with if (!$canManage) { $writeDenied(); }:

Route Purpose New guard
POST / create tracker 403 for non-manager
PUT /{id} update tracker 403 for non-manager
DELETE /{id} delete tracker 403 for non-manager
POST /oauth/create GitHub tracker create + project link 403 for non-manager

GET / additionally returns "canManage" so the front-end can hide the write UI.

Client-side fix (gui/templates/issuetracker/issuetrackerView.html)

Same pattern as reqMgrSystemView.html / platformsView.html:

  • canManage = !!r.canManage captured from the BFF payload.
  • #btnCreate and #btnGithub toggled via $('#btnCreate').toggle(canManage).
  • Edit/Delete icons emitted only when managing; the Actions column (header + cell) is dropped for view-only users.
  • View-only users also get a bare plain-text table, exactly like legacy 1.9.20.
  • Defense-in-depth guards inside showCreateModal, editTracker, deleteTracker, githubOAuthStart, createGithubTracker and checkOAuthConnected.

No new i18n keys — every label reuses existing keys present in all 10 locale bundles.

Verification

admin full UI

Admin (manager): full UI, canManage:true, create/delete work (HTTP 200).

itview view-only bare table

itview (view-only): the same tracker row renders as plain text — no Actions column, no Edit/Delete icons, no Create/GitHub buttons.

itview denied writes

itview blocked: POST/PUT/DELETE/oauth/create → HTTP 403 {"status":"error","message":"No permission"}.

  • 4 AUDIT events logged for the denied writes (activity=EDIT, object_type=issuetrackers).
  • Event Viewer clean: log_level IN (1,2) → 0 rows.
  • Test suite: tmp/TLU_Test_Cases.md → "Task — Issue #960" (all PASS).