# Request Validation — Portal Policy Template Bundles

This folder contains **request-validation** policy template bundles for the Layer7 API Developer Portal. Each bundle is intended to demonstrate Portal-aware patterns for validating an incoming API request before it reaches the backend — e.g. validating it against the API's published OpenAPI specification, against a JSON Schema, against an XML Schema, or against custom rules. The Portal Templates Guide places this kind of template in the **Message Validation** category, alongside `Validate JSON Schema` and `Validate XML Schema` (see the [Layer7 API Developer Portal Policy Templates Guide](../Layer7%20API%20Developer%20Portal%20Policy%20Templates%20Guide.md) for category guidance).

## Bundles in this folder

### `ValidateJSONSchema-1.0.0.json`
Installs the **`ValidateJSONSchema-1.0`** template. Validates the request body against a publisher-supplied **JSON Schema (Draft v4)** using the Layer7 `Validate JSON Schema` assertion (`<L7p:JSONSchemaAssertion>`). The schema is provided by the publisher as the mandatory `JSON Schema (Draft v4) *` input and is applied via the assertion's *Configure-in-advance* source (`<L7p:ResourceInfo staticResourceInfo="included">`). On a validation failure, the fragment captures the assertion-set `${jsonschema.failure}` variable, returns **`400 Bad Request`** with a JSON error body of the form `{"error":{"code":400,"message":"Request body failed JSON Schema validation: <detail>"}}`, sets `portal.analytics.response.code` to `400`, and exports `errorMsg` plus the analytics variables for downstream consumption.

**Recommended placement:** AFTER threat protection (so `Protect Against JSON Document Structure Threats` short-circuits malformed payloads before schema parsing) and BEFORE rate-quota / routing.

**Arguments (inputs)**

| Visible / Hidden | Name | Notes |
| --- | --- | --- |
| hidden | `portal.managed.service.apiId` | Auto-populated by the Portal; used to set `portal.analytics.api.uuid`. |
| **visible** | `schema *` (`JSON Schema (Draft v4) *`) | The full JSON Schema document (string). Variables are expanded at runtime per the assertion's `Configure-in-advance` semantics. |

**Exported context variables (outputs):** `errorMsg`, `portal.analytics.action`, `portal.analytics.api.uuid`, `portal.analytics.response.code`.

**Customising:**
- **URL-based schema retrieval.** Replace the `<L7p:ResourceInfo staticResourceInfo="included"><L7p:Document stringValue="${schema}"/></L7p:ResourceInfo>` block with `<L7p:ResourceInfo singleUrlResourceInfo="included"><L7p:Url stringValue="${schemaUrl}"/></L7p:ResourceInfo>` and add a `schemaUrl` encass argument so the publisher supplies a URL instead of an inline schema. Useful when schemas live in a central repo or a CDN.
- **Schema version.** The bundle pins `<L7p:JsonSchemaVersion stringValue="DRAFTV4"/>` (Draft v4 is the recommended version per the Layer7 docs - Draft v2 was deprecated in Gateway 10.0 CR1). Edit if your tooling produces a different draft.
- **Don't echo the failure detail.** The default error body includes `${jsonschema.failure}` for ease of integration testing. For production, change the `errorMsg` SetVariable in the failure branch to a generic message (e.g. `"Request body did not match expected schema."`) so a probing attacker cannot enumerate schema constraints.

### `ValidateXMLSchema-1.0.0.json`
Installs the **`ValidateXMLSchema-1.0`** template. Validates the request body against a publisher-supplied **XML Schema (XSD)** using the Layer7 `Validate Against Schema` assertion (`<L7p:SchemaValidation>`). The schema is provided by the publisher as the mandatory `XML Schema (XSD) *` input and is applied via the assertion's *Configure-in-advance* source (`<L7p:ResourceInfo staticResourceInfo="included">`). The bundle sets `<L7p:ApplyToArguments booleanValue="false"/>` so the schema is applied to the entire message - suitable for both SOAP and non-SOAP XML APIs. On a validation failure, the fragment captures the assertion-set `${schema.failure}` variable, returns **`400 Bad Request`** with a JSON error body of the form `{"error":{"code":400,"message":"Request body failed XML Schema validation: <detail>"}}`, sets `portal.analytics.response.code` to `400`, and exports `errorMsg` plus the analytics variables.

**Recommended placement:** AFTER threat protection (so `Protect Against Document Structure Threats` short-circuits oversized XML before schema parsing) and BEFORE rate-quota / routing.

**Arguments (inputs)**

| Visible / Hidden | Name | Notes |
| --- | --- | --- |
| hidden | `portal.managed.service.apiId` | Auto-populated by the Portal; used to set `portal.analytics.api.uuid`. |
| **visible** | `schema *` (`XML Schema (XSD) *`) | The full XSD document (string). |

**Exported context variables (outputs):** `errorMsg`, `portal.analytics.action`, `portal.analytics.api.uuid`, `portal.analytics.response.code`.

**Customising:**
- **SOAP envelope or arguments validation.** For SOAP services, set `<L7p:ApplyToArguments booleanValue="true"/>` (RPC-style argument validation) and / or change the validation target (the Layer7 doc covers `ENVELOPE` / `BODY` / `ARGUMENTS` modes). Add a `<L7p:ValidationTarget stringValue="BODY"/>` element next to `<L7p:Target target="REQUEST"/>` to target only the SOAP body.
- **URL-based schema retrieval.** Same pattern as `ValidateJSONSchema-1.0.0.json` - swap the `staticResourceInfo` block for a `singleUrlResourceInfo` block and add a `schemaUrl` encass argument.
- **Global Schemas.** For schemas you want to share across many APIs, use the Gateway's *Manage Global Resources* capability and switch the assertion's `resourceInfo` to a global-schema reference rather than inline content.
- **Generic error message.** As with the JSON variant, the default error body echoes `${schema.failure}`. Switch to a generic message in the failure branch for production.

> **Note on L7p element names:** the L7p XML element names used in these two bundles (`<L7p:JSONSchemaAssertion>` and `<L7p:SchemaValidation>`) are derived from Layer7's documented Graphman `configName` values (`JSONSchema` and `SchemaValidation`) and from the L7p naming conventions observed in the other Portal Template bundles in this repo. If your Gateway version reports an "unknown element" error on import, export a small test policy that uses the corresponding assertion from your Policy Manager and update the element names in this bundle to match exactly.

### `ValidateOpenAPISample-1.0.0.json`
Installs the **`ValidateOpenAPISample-1.0`** template. Validates an incoming request against the OpenAPI 3.x specification that the Portal publishes for each API in the service property `service.property.internal.apiSpec`. The bundle wraps a single `Validate Against OpenAPI Document` assertion (`<L7p:OpenApi>`) inside a four-way `OneOrMore` switch that is driven by a Portal **drop-down** input argument (`l7.enum.option`) called *Validation Profile*. Each branch sets every option on the underlying assertion explicitly so that the assertion's full option surface is visible at a glance — this makes the bundle a useful starting point for any team that wants to drop OpenAPI validation into their own template and tune it from there.

**Validation profiles offered to the publisher**

| Profile | `ValidatePath` | `ValidateMethod` | `ValidateHeaders` | `ValidateQueryParameters` | `ValidateBody` | `RequireSecurityCredentials` | `StrictValidation` |
| --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| `path` | true | false | false | false | false | false | false |
| `path_and_method` | true | true | false | false | false | false | false |
| `request` | true | true | true | true | true | true | false |
| `request_strict` | true | true | true | true | true | true | true |

`UseOpenApiDocAsUrl` is set to `false` in every branch (the spec comes from a context variable, not a URL) and `Prefix` is set to the assertion default `openapi`, so the result variables are exposed as `${openapi.path}`, `${openapi.expandedPath}`, `${openapi.servers}`, `${openapi.errors}`, and `${openapi.errorDetails}`.

If none of the profiles match, **or** the selected profile's `Validate Against OpenAPI Document` assertion fails, the fallback branch logs the validation failure via *Audit Detail* and returns a `500 application/json` body of the form:

```json
{
  "error": {
    "code": 500,
    "message": "OpenAPI spec validation failed. Profile: <profile>"
  }
}
```

**Arguments:** `l7.enum.option` (the *Validation Profile* drop-down, mandatory — referenced as `${l7.enum.option}`), and four option arguments — `l7.enum.option.path`, `l7.enum.option.path_and_method`, `l7.enum.option.request`, `l7.enum.option.request_strict` — that populate the drop-down menu using the `l7.enum.<name>` / `l7.enum.<name>.<value>` convention (see `../DropDown/README.md` for the convention itself).
**Exported context variables:** `openapi.errors`, `openapi.expandedPath`, `openapi.path`, `openapi.servers` (declared as encass results so a parent template can read them).

## All `Validate Against OpenAPI Document` options reference

These are every option supported by the `<L7p:OpenApi>` assertion in Gateway 11.2; each one is set explicitly in every branch of `ValidateOpenAPISample-1.0` so they're easy to find and edit:

| Option | Default | Notes |
| --- | :---: | --- |
| `OpenApiDoc` | _required_ | Name of a context variable holding the OpenAPI 3.x spec (or a URL when `UseOpenApiDocAsUrl=true`). For Portal-managed APIs this is `service.property.internal.apiSpec`. |
| `UseOpenApiDocAsUrl` | `false` | When `true`, treats `OpenApiDoc` as a URL to download the spec from (Gateway 10.0 CR4+). |
| `Prefix` | `openapi` | Prefix for output context variables (e.g. `openapi.errors`, `openapi.path`). Configure unique prefixes if you need to run multiple instances of the assertion in the same policy. |
| `ServiceBase` | _current service URI_ | Service URI used to identify the service. Must end with `/*` to be resolvable. |
| `ValidatePath` | `true` | Validate the request URI against a defined operation. **Disabling this disables all other validation options.** |
| `ValidateMethod` | `true` | Check that the request HTTP method is allowed for the resolved path. Only effective when `ValidatePath=true`. |
| `ValidateHeaders` | `false` | Validate request headers against the spec's header parameters. |
| `ValidateQueryParameters` | `false` | Validate query-parameter names and types. |
| `ValidateBody` | `false` | Validate JSON request body against the operation's schema. |
| `RequireSecurityCredentials` | `true` | Verify that the security credentials defined in the spec (API key, OAuth, etc.) are present in the request. **Does not authenticate them** — pair with the bundles in `../Authentication/` for that. Only effective when `ValidateMethod=true`. |
| `StrictValidation` | `false` | Reject undefined query parameters and additional body properties. |

For the full Broadcom doc page, see [Validate Against OpenAPI Document Assertion](https://techdocs.broadcom.com/us/en/ca-enterprise-software/layer7-api-management/api-gateway/11-2/policy-assertions/assertion-palette/message-validation-transformation-assertions/validate-against-openapi-document-assertion.html).

## Extending the bundle

- **Response validation.** This bundle validates incoming requests only. The same `<L7p:OpenApi>` assertion can validate outgoing responses (path + method + status + body + headers) by switching the assertion's *Target Message* to `Response`. If you need response validation, duplicate one of the `request` branches, change its target, and add `<L7p:Target target="RESPONSE"/>` plus a unique `Prefix` (e.g. `openapi.response`) so the result variables don't collide with request-side variables.
- **Composing with other templates.** Because the four `openapi.*` results are declared as encass results and exported via *Export Variables from Fragment*, a parent Portal template can read them and decide what to do — e.g. include them in a logging template, or skip routing if `${openapi.errors}` is non-empty.
- **Adding a new profile.** To add a fifth profile (e.g. `headers_only`), copy the existing `l7.enum.option.path` argument, rename it to `l7.enum.option.headers_only`, set the next `ordinal`, and add a matching `<wsp:All>` branch in the policy that compares `${option}` to `headers_only` and configures the `<L7p:OpenApi>` block accordingly.

## Conventions

Bundles in this folder follow the same conventions as the other Portal Templates in this repository:

- Distributed as a `*-<version>.json` policy bundle (schema `11.2.1`, `defaultAction: NEW_OR_UPDATE`).
- Installs a policy fragment under `/Portal Templates/` and an encapsulated assertion in the Policy Manager assertion palette (`customAssertions` palette folder).
- Surface every option of the underlying assertion explicitly in the policy XML so the bundle doubles as documentation of the assertion's option set.

Contributions are welcome — drop a new `*-<version>.json` bundle into this folder and add a corresponding entry in the **Bundles in this folder** section above.
