Skip to content

[9.4] [Synthetics] Add "Run tests manually" sub-feature privilege (monitor:run-manually) (#282149) - #290407

Merged
shahzad31 merged 1 commit into
elastic:9.4from
shahzad31:backport/9.4/pr-282149
Sep 11, 2026
Merged

[9.4] [Synthetics] Add "Run tests manually" sub-feature privilege (monitor:run-manually) (#282149)#290407
shahzad31 merged 1 commit into
elastic:9.4from
shahzad31:backport/9.4/pr-282149

Conversation

@shahzad31

Copy link
Copy Markdown
Contributor

Backport

This will backport the following commits from main to 9.4:

Questions ?

Please refer to the Backport tool documentation

…run-manually) (elastic#282149)

## 📝 Summary

Let a **read-only** Synthetics user **trigger manual test runs** ("Run
test manually") without granting create/edit/delete access.

Today, running a test requires `uptime-write` (bundled into `all`), so
users who only need to trigger ad-hoc runs must be granted full write
just to click "Run test manually". This adds a granular **`Can run tests
manually`** sub-feature.

## 🧠 Design — OR-authz, fully non-breaking

"Run test manually" (`POST /api/synthetics/monitor/test/{monitorId}`) is
**read-plus-execute**: it decrypts the existing monitor and dispatches a
one-off run via the synthetics service. It never mutates the monitor
saved object — it only required `uptime-write` because of the blanket
"non-GET ⇒ write" default.

Instead of *moving* the capability out of `all` (which breaks customized
`minimal_all` roles — see the sibling global-params PR), this route uses
an **OR-set**:

```ts
// synthetics_route_wrapper builds:
requiredPrivileges: ['uptime-read', { anyRequired: ['uptime-write', 'monitor-run-manually'] }]
```

→ the route needs `uptime-read` **AND** (`uptime-write` **OR**
`monitor-run-manually`). This means:

| Role | Runs a test? | Why |
|---|---|---|
| Base `all` | ✅ | has `uptime-write` |
| Customized `minimal_all` | ✅ | still has `uptime-write` |
| `read` only | ❌ (unchanged) | has neither |
| **`read` + `Can run tests manually`** | ✅ | has `monitor-run-manually`
|

So it is **fully non-breaking** (nothing is removed from any existing
role) and **purely additive** — the `can_run_test_manually` sub-feature
is `includeIn: 'none'`, granted only when an admin explicitly toggles it
onto a role.

## 🧩 What changed

| Layer | Change |
|---|---|
| **Route wrapper** | `synthetics_route_wrapper` now emits an `{
anyRequired }` set when a route sets `anyRequiredPrivileges` (new
`types.ts` field). |
| **Feature** | New `MONITOR_RUN_MANUALLY_API = 'monitor-run-manually'`
+ `can_run_test_manually` sub-feature (`includeIn: 'none'`, UI cap
`canRunTestManually`). |
| **Route** | `test_now_monitor` → `writeAccess: false` +
`anyRequiredPrivileges: ['uptime-write', 'monitor-run-manually']`. |
| **UI** | "Run test manually" (overview popover + monitor detail)
enabled for `canEditSynthetics` **OR** `canRunTestManually`. |
| **Tests** | Focused authz spec (read→403, read+run→404, `all`→404,
no-access→403); route-auth sweep extended for OR-sets; privilege
snapshots. |

## 🧪 Testing

Verified locally against a real ES + Kibana stack (Scout API tests):

- ✅ `read`-only role → **403** on `POST /monitor/test/{id}` with
`[uptime-write,monitor-run-manually]`.
- ✅ `read` + `can_run_test_manually` role → authorized (**404** for a
non-existent monitor, i.e. past the authz layer).
- ✅ Base `all` role → still authorized (non-breaking).
- ✅ Route-auth sweep passes with the new OR-set handling.

## 🚦 Release note

Synthetics: added a **Run tests manually** sub-feature privilege that
lets an otherwise read-only role trigger on-demand test runs of existing
monitors, without granting monitor write access. Existing roles are
unaffected.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit e264fc0)

# Conflicts:
#	x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitor_details/run_test_manually.tsx
#	x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx
#	x-pack/solutions/observability/plugins/synthetics/public/hooks/use_capabilities.ts
#	x-pack/solutions/observability/plugins/synthetics/test/scout/monitor_crud/api/tests/synthetics_api_security.spec.ts
@shahzad31 shahzad31 added the backport This PR is a backport of another PR label Sep 10, 2026
@shahzad31
shahzad31 enabled auto-merge (squash) September 10, 2026 21:55
@kibanamachine
kibanamachine requested review from elena-shostak and removed request for kibanamachine September 10, 2026 21:56
@kibanamachine

Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
synthetics 1.1MB 1.1MB +276.0B

@shahzad31
shahzad31 merged commit 4a99eec into elastic:9.4 Sep 11, 2026
60 checks passed
@shahzad31
shahzad31 deleted the backport/9.4/pr-282149 branch September 11, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport This PR is a backport of another PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants