diff --git a/docs/web/apis.adoc b/docs/web/apis.adoc index 2261870c..88a02415 100644 --- a/docs/web/apis.adoc +++ b/docs/web/apis.adoc @@ -100,9 +100,9 @@ A Universal API can be exposed through three *independent* axes — they don't s . *Service point* — the API is mounted into a `site`, `webapp`, or `admin tool` service via that service's descriptor. . *Web endpoint* — the API opts in with `mount: "web"` and becomes reachable on the *Web* endpoint (the public XP port) under `/api/`. -. *Management endpoint* — the API opts in with `mount: "management"` and becomes reachable on the *Management* endpoint, which listens on its own port. +. *Management endpoint* — the API opts in with `mount: "management"` and becomes reachable on the *Management* endpoint, which listens on its own port, at the root: `+/:+`. -The `/api/` URL prefix is the same on both endpoints, but the two are served on *different ports*. The Management endpoint is not reachable through the Web endpoint and vice versa. +The two endpoints are served on *different ports*, and the URL forms differ: `/api/` prefix on the Web endpoint, no prefix on the Management endpoint. The Management endpoint is not reachable through the Web endpoint and vice versa. [#service-point] == Service point @@ -173,7 +173,7 @@ WARNING: The `mount: "management"` feature is experimental and subject to change NOTE: The Management endpoint is for *advanced use only*. It is intended for extending XP's management capabilities (cluster operations, internal tooling, infrastructure integrations) — not for regular application APIs. -An API can also be exposed on the *Management* endpoint by adding `"management"` to its `mount` list. The Management endpoint is a separate listener on its own port, distinct from the Web endpoint — use it for internal or operational APIs that should not be reachable from the public network. +An API can also be exposed on the *Management* endpoint by adding `"management"` to its `mount` list. The Management endpoint is a separate listener on its own port, distinct from the Web endpoint — use it for internal or operational APIs that should not be reachable from the public network. There the API is served at the root of the port, `+/:+`, beside XP's own <> (`server:app`, `server:snapshot`, ...). Every request must carry an authenticated user matching `allow`. .src/main/resources/apis//.yaml [source,yaml] @@ -187,9 +187,40 @@ allow: Both mounts can be combined: `mount: ["web", "management"]` makes the API reachable on both endpoints. The `allow` principal list is enforced on every request regardless of which endpoint served it. +[#management-policy] +=== Honouring virtual host policy + +image:xp-8020.svg[XP 8.2.0,opts=inline] A <> selects what each API exposes through it. The mapping declares settings under `+context.api.\:.+` (the colon escaped, as in any properties file); XP copies them into the request context as attributes named `+api.:.+`. XP's own resources understand `verbs`, the comma separated list of operations the mapping exposes. An API of your own reads its settings from the same attributes and defines their meaning. An absent setting means unrestricted: a request without a matching mapping, or on the default management port, carries none. + +.src/main/resources/apis/cluster/cluster.ts +[source,typescript] +---- +import {get as getContext} from '/lib/xp/context'; + +const KEY = 'com.example.ops:cluster'; + +function allows(verb: string): boolean { + const value = getContext().attributes[`api.${KEY}.verbs`]; + if (typeof value !== 'string' || value.trim() === '') { + return true; + } + const verbs = value.split(',').map((v) => v.trim()); + return verbs.includes('*') || verbs.includes(verb); +} + +export function GET(request: XP.Request): XP.Response { + if (!allows('list')) { + return {status: 403, body: {message: 'Verb [list] is not exposed on this virtual host'}}; + } + return {body: {nodes: listNodes()}}; +} +---- + +With `+mapping.ops.context.api.com.example.ops\:cluster.verbs = list+`, the mapping exposes `list` and nothing else the API guards. + == API discovery -`GET /api` (or `GET /api/`) returns a JSON index of every API mounted on the *endpoint that received the request*. Calling it on the Web endpoint lists APIs with `"web"` in `mount`; calling it on the Management endpoint lists APIs with `"management"` in `mount`. The response is a `resources` array, one entry per API: +`GET /api` (or `GET /api/`) on the Web endpoint returns a JSON index of every API with `"web"` in `mount`. There is no index on the Management endpoint. The response is a `resources` array, one entry per API: [source,json] ---- diff --git a/docs/web/images/xp-820.svg b/docs/web/images/xp-820.svg new file mode 100644 index 00000000..cf359793 --- /dev/null +++ b/docs/web/images/xp-820.svg @@ -0,0 +1 @@ +XPXP8.2.08.2.0