``logout`` calls ``clearAtomStorage(systemConfigAtom)`` which sets the
atom to ``null``, and on a fresh SPA login (no full reload)
``app.tsx``'s ``fetchSystemConfig`` doesn't re-run, so consumers that
read ``systemConfig.xxx`` directly crash with "Cannot read properties
of null". A hard refresh masks the bug. Add optional chaining at the
read sites (grafana url / dashboard uids, showMonitoring) so the
contract stays robust regardless of how the atom is reset.
The OSS access-control form currently lets a plugin replace the
`allowed_users` policy entry. Multi-tenancy also wants:
- an extra radio option for "Org-scoped" sitting in front of the
built-ins, and
- a context-sensitive create-time default (routes inside a non-
platform Org should land on the Org-scoped policy rather than
`authed`).
Extend the `accessControl` slot with two more hooks:
- `prependedPolicies?: { policyValue, labelId, tipsId?, Field? }[]`
— entries prepended to the radio group; each may carry an
optional content `Field` rendered when selected.
- `resolveCreateDefault?: () => string | undefined` — overrides the
initial `access_policy` for the create flow; the OSS fallback is
still `authed`.
The host's tooltip list mirrors the same order. Existing behaviour
without a plugin is unchanged.
Reopening the Access Settings dialog after a save could leave the
radio on the wrong policy: the form initialized `access_policy`
from the route list's snapshot, but the list isn't refreshed when
the dialog closes, so the snapshot's value silently shadowed the
just-saved policy.
Backend now returns the route's authoritative `access_policy` on
`GET /v2/model-routes/{id}/access`. Use that value when the GET
resolves, falling back to the parent snapshot only as the initial
seed so the radio isn't briefly unselected.
While we're here, widen the `access_policy` type from a closed
union to `string` — plugins can contribute additional policy
values via the `accessControl.allowedUsersOverride` slot, so the
wire type shouldn't restrict to the OSS-side enum.
The OSS Access Settings form hardcodes the `allowed_users` policy
(per-user explicit grants via the legacy transfer widget). Multi-
tenancy needs a different surface that grants by Org / Group / User
principals via the `/v2/model-routes/{id}/principals` endpoints, and
the enterprise plugin will ship that — but the OSS form should stay
visually unchanged when no plugin is loaded.
Add a plugin extension point on the form: if a registered plugin
exposes `accessControl.allowedUsersOverride: { policyValue, labelId,
tipsId?, Field }`, the form replaces the `allowed_users` radio entry
with the plugin's labelled option, swaps the access-scope tooltip
copy, and renders `<Field form routeId action />` for the override
policy. Without a plugin the form's radio / tooltip / content path
are unchanged.
`handleOnFinish` continues to send `users` only for `allowed_users`;
any other policy (including the plugin's value) goes through with
an empty users list, leaving the plugin's Field to manage its own
principal CRUD inline.
The form populated its candidate list via the admin-only
`GET /v2/users`, so opening Route Access Settings as a non-admin
caller (e.g. an org owner managing their own route in the
enterprise plugin) returned 403. Add a `queryUserDirectory` client
wrapper around the slim `/v2/user-directory` endpoint (gated to
platform admin OR org owner) and switch `getUserList` over. Response
shape is the same so the rest of the form is unchanged.