diff --git a/compiler/model/metamodel.ts b/compiler/model/metamodel.ts index 13c325aa70..ca5bb0a52e 100644 --- a/compiler/model/metamodel.ts +++ b/compiler/model/metamodel.ts @@ -366,6 +366,10 @@ export class Endpoint { visibility?: Visibility accept?: string[] contentType?: string[] + securityPrerequisites?: { + index?: string[] + cluster?: string[] + } } export class UrlTemplate { diff --git a/compiler/model/utils.ts b/compiler/model/utils.ts index b42c0495bf..727029b3a1 100644 --- a/compiler/model/utils.ts +++ b/compiler/model/utils.ts @@ -544,7 +544,7 @@ export function hoistRequestAnnotations ( request: model.Request, jsDocs: JSDoc[], mappings: Record, response: model.TypeName | null ): void { const knownRequestAnnotations = [ - 'since', 'rest_spec_name', 'stability', 'visibility', 'behavior', 'class_serializer', 'doc_id' + 'since', 'rest_spec_name', 'stability', 'visibility', 'behavior', 'class_serializer', 'security_prerequisites_index', 'security_prerequisites_cluster', 'doc_id' ] // in most of the cases the jsDocs comes in a single block, // but it can happen that the user defines multiple single line jsDoc. @@ -584,6 +584,33 @@ export function hoistRequestAnnotations ( } else if (tag === 'since') { assert(jsDocs, semver.valid(value), `Request ${request.name.name}'s @since is not valid semver: ${value}`) endpoint.since = value + } else if (tag === 'security_prerequisites_index') { + const privileges = [ + 'all', 'auto_configure', 'create', 'create_doc', 'create_index', 'delete', 'delete_index', 'index', + 'maintenance', 'manage', 'manage_follow_index', 'manage_ilm', 'manage_leader_index', 'monitor', + 'read', 'read_cross_cluster', 'view_index_metadata', 'write' + ] + const values = value.split(',').map(v => v.trim()) + for (const v of values) { + assert(jsDocs, privileges.includes(v), `The index privilege '${v}' does not exists.`) + } + endpoint.securityPrerequisites = endpoint.securityPrerequisites ?? {} + endpoint.securityPrerequisites.index = values + } else if (tag === 'security_prerequisites_cluster') { + const privileges = [ + 'all', 'cancel_task', 'create_snapshot', 'grant_api_key', 'manage', 'manage_api_key', 'manage_ccr', + 'manage_ilm', 'manage_index_templates', 'manage_ingest_pipelines', 'manage_logstash_pipelines', + 'manage_ml', 'manage_oidc', 'manage_own_api_key', 'manage_pipeline', 'manage_rollup', 'manage_saml', + 'manage_security', 'manage_service_account', 'manage_slm', 'manage_token', 'manage_transform', + 'manage_watcher', 'monitor', 'monitor_ml', 'monitor_rollup', 'monitor_snapshot', 'monitor_text_structure', + 'monitor_transform', 'monitor_watcher', 'read_ccr', 'read_ilm', 'read_pipeline', 'read_slm', 'transport_client' + ] + const values = value.split(',').map(v => v.trim()) + for (const v of values) { + assert(jsDocs, privileges.includes(v), `The cluster privilege '${v}' does not exists.`) + } + endpoint.securityPrerequisites = endpoint.securityPrerequisites ?? {} + endpoint.securityPrerequisites.cluster = values } else if (tag === 'doc_id') { assert(jsDocs, value.trim() !== '', `Request ${request.name.name}'s @doc_id is cannot be empty`) endpoint.docId = value diff --git a/docs/modeling-guide.md b/docs/modeling-guide.md index e8d3aed172..e0b460cad8 100644 --- a/docs/modeling-guide.md +++ b/docs/modeling-guide.md @@ -484,3 +484,35 @@ export interface Request extends RequestBase { body?: TDocument } ``` + +#### `@security_prerequisites_index` + +If an endpoint has some index security prerequisites to satisfy, you can specify them here with a comma separated list. + +```ts +/** + * @rest_spec_name indices.create + * @since 0.0.0 + * @stability stable + * @security_prerequisites_index create_index, manage + */ +export interface Request extends RequestBase { + ... +} +``` + +#### `@security_prerequisites_cluster` + +If an endpoint has some cluster security prerequisites to satisfy, you can specify them here with a comma separated list. + +```ts +/** + * @rest_spec_name cluster.state + * @since 1.3.0 + * @stability stable + * @security_prerequisites_cluster monitor, manage + */ +export interface Request extends RequestBase { + ... +} +``` diff --git a/output/schema/schema.json b/output/schema/schema.json index 9833dd2b44..a365cddbda 100644 --- a/output/schema/schema.json +++ b/output/schema/schema.json @@ -1969,6 +1969,12 @@ "name": "Response", "namespace": "cluster.state" }, + "securityPrerequisites": { + "cluster": [ + "monitor", + "manage" + ] + }, "since": "1.3.0", "stability": "stable", "urls": [ @@ -3722,6 +3728,12 @@ "name": "Response", "namespace": "indices.create" }, + "securityPrerequisites": { + "index": [ + "create_index", + "manage" + ] + }, "since": "0.0.0", "stability": "stable", "urls": [ diff --git a/specification/cluster/state/ClusterStateRequest.ts b/specification/cluster/state/ClusterStateRequest.ts index 5c67e5f862..155388bff0 100644 --- a/specification/cluster/state/ClusterStateRequest.ts +++ b/specification/cluster/state/ClusterStateRequest.ts @@ -30,6 +30,7 @@ import { Time } from '@_types/Time' * @rest_spec_name cluster.state * @since 1.3.0 * @stability stable + * @security_prerequisites_cluster monitor, manage */ export interface Request extends RequestBase { path_parts: { diff --git a/specification/indices/create/IndicesCreateRequest.ts b/specification/indices/create/IndicesCreateRequest.ts index 15996b9225..923d081310 100644 --- a/specification/indices/create/IndicesCreateRequest.ts +++ b/specification/indices/create/IndicesCreateRequest.ts @@ -29,6 +29,7 @@ import { Time } from '@_types/Time' * @rest_spec_name indices.create * @since 0.0.0 * @stability stable + * @security_prerequisites_index create_index, manage */ export interface Request extends RequestBase { path_parts: {