mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Set spaces and roles CRUD APIs to public (#193534)
Closes #192153 ## Summary This PR sets the spaces and roles CRUD operation HTTP API endpoints to public in both stateful and serverless offerings, and additionally, switches to the versioned router to register these endpoints. Prior to this PR, the access level was not explicitly set, thus any endpoints registered in serverless were by default internal. CRUD operations for spaces and roles are being set to public to support the rollout of custom roles in serverless, which coincides with enabling multiple spaces. ### Note - Currently, roles APIs are only available in serverless via a feature flag (`xpack.security.roleManagementEnabled`) - Spaces APIs are already registered in serverless, however, the maximum number of spaces is by default 1, rendering create and delete operations unusable. By overriding `xpack.spaces.maxSpaces` to a number greater than 1 (stateful default is 1000), it will effectively enable use of the spaces CRUD operations in serverless. ## Tests - x-pack/test_serverless/api_integration/test_suites/common/management/multiple_spaces_enabled.ts - x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts - x-pack/test_serverless/api_integration/test_suites/common/platform_security/authorization.ts - x-pack/test_serverless/api_integration/test_suites/common/platform_security/roles_routes_feature_flag.ts - Unit tests for each endpoint (to account for versioned router) - Flaky Test Runner: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7002 ## Manual Testing 1. Start ES & Kibana in serverless mode with config options to enable role management and multiple spaces Elasticsearch: ``` xpack.security.authc.native_roles.enabled: true ``` KIbana: ``` xpack.security.roleManagementEnabled: true xpack.spaces.maxSpaces: 100 ``` 3. Issue each CRUD HTTP API without including the internal origin header ('x-elastic-internal-origin') and verify you do not receive a 400 with the message "method [get|post|put|delete] exists but is not available with the current configuration" 4. Repeat steps 1 & 2 from the current head of main and verify that you DO receive a 400 with the message "method [get|post|put|delete] exists but is not available with the current configuration" Regression testing - ensure that interfaces which leverage spaces and roles APIs are functioning properly - Spaces management - Space navigation - Roles management --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c3364db26c
commit
26f2928b08
44 changed files with 5121 additions and 673 deletions
|
@ -5,7 +5,7 @@ set -euo pipefail
|
|||
source .buildkite/scripts/common/util.sh
|
||||
|
||||
echo --- Capture OAS snapshot
|
||||
cmd="node scripts/capture_oas_snapshot --include-path /api/status --include-path /api/alerting/rule/ --include-path /api/alerting/rules --include-path /api/actions"
|
||||
cmd="node scripts/capture_oas_snapshot --include-path /api/status --include-path /api/alerting/rule/ --include-path /api/alerting/rules --include-path /api/actions --include-path /api/security/role --include-path /api/spaces"
|
||||
if is_pr && ! is_auto_commit_disabled; then
|
||||
cmd="$cmd --update"
|
||||
fi
|
||||
|
|
1427
oas_docs/bundle.json
1427
oas_docs/bundle.json
File diff suppressed because it is too large
Load diff
|
@ -6263,6 +6263,323 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/api/spaces/space": {
|
||||
"get": {
|
||||
"description": "Get all spaces",
|
||||
"operationId": "%2Fapi%2Fspaces%2Fspace#0",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "The version of the API to use",
|
||||
"in": "header",
|
||||
"name": "elastic-api-version",
|
||||
"schema": {
|
||||
"default": "2023-10-31",
|
||||
"enum": [
|
||||
"2023-10-31"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "purpose",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"any",
|
||||
"copySavedObjectsIntoSpace",
|
||||
"shareSavedObjectsIntoSpace"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "include_authorized_purposes",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"nullable": true,
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
false
|
||||
],
|
||||
"type": "boolean",
|
||||
"x-oas-optional": true
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"x-oas-optional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {},
|
||||
"summary": "",
|
||||
"tags": [
|
||||
"spaces"
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a space",
|
||||
"operationId": "%2Fapi%2Fspaces%2Fspace#1",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "The version of the API to use",
|
||||
"in": "header",
|
||||
"name": "elastic-api-version",
|
||||
"schema": {
|
||||
"default": "2023-10-31",
|
||||
"enum": [
|
||||
"2023-10-31"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "A required header to protect against CSRF attacks",
|
||||
"in": "header",
|
||||
"name": "kbn-xsrf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"example": "true",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json; Elastic-Api-Version=2023-10-31": {
|
||||
"schema": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"_reserved": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"disabledFeatures": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"initials": {
|
||||
"maxLength": 2,
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {},
|
||||
"summary": "",
|
||||
"tags": [
|
||||
"spaces"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/spaces/space/{id}": {
|
||||
"delete": {
|
||||
"description": "Delete a space",
|
||||
"operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "The version of the API to use",
|
||||
"in": "header",
|
||||
"name": "elastic-api-version",
|
||||
"schema": {
|
||||
"default": "2023-10-31",
|
||||
"enum": [
|
||||
"2023-10-31"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "A required header to protect against CSRF attacks",
|
||||
"in": "header",
|
||||
"name": "kbn-xsrf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"example": "true",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {},
|
||||
"summary": "",
|
||||
"tags": [
|
||||
"spaces"
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"description": "Get a space",
|
||||
"operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "The version of the API to use",
|
||||
"in": "header",
|
||||
"name": "elastic-api-version",
|
||||
"schema": {
|
||||
"default": "2023-10-31",
|
||||
"enum": [
|
||||
"2023-10-31"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {},
|
||||
"summary": "",
|
||||
"tags": [
|
||||
"spaces"
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"description": "Update a space",
|
||||
"operationId": "%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "The version of the API to use",
|
||||
"in": "header",
|
||||
"name": "elastic-api-version",
|
||||
"schema": {
|
||||
"default": "2023-10-31",
|
||||
"enum": [
|
||||
"2023-10-31"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "A required header to protect against CSRF attacks",
|
||||
"in": "header",
|
||||
"name": "kbn-xsrf",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"example": "true",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json; Elastic-Api-Version=2023-10-31": {
|
||||
"schema": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"_reserved": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"disabledFeatures": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"initials": {
|
||||
"maxLength": 2,
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {},
|
||||
"summary": "",
|
||||
"tags": [
|
||||
"spaces"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/status": {
|
||||
"get": {
|
||||
"operationId": "%2Fapi%2Fstatus#0",
|
||||
|
@ -6360,6 +6677,9 @@
|
|||
{
|
||||
"name": "connectors"
|
||||
},
|
||||
{
|
||||
"name": "spaces"
|
||||
},
|
||||
{
|
||||
"name": "system"
|
||||
}
|
||||
|
|
|
@ -16241,6 +16241,214 @@ paths:
|
|||
tags:
|
||||
- Security AI Assistant API
|
||||
- Prompts API
|
||||
/api/spaces/space:
|
||||
get:
|
||||
description: Get all spaces
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: query
|
||||
name: purpose
|
||||
required: false
|
||||
schema:
|
||||
enum:
|
||||
- any
|
||||
- copySavedObjectsIntoSpace
|
||||
- shareSavedObjectsIntoSpace
|
||||
type: string
|
||||
- in: query
|
||||
name: include_authorized_purposes
|
||||
required: true
|
||||
schema:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- enum:
|
||||
- false
|
||||
type: boolean
|
||||
x-oas-optional: true
|
||||
- type: boolean
|
||||
x-oas-optional: true
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
post:
|
||||
description: Create a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/spaces/space/{id}:
|
||||
delete:
|
||||
description: Delete a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
get:
|
||||
description: Get a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
put:
|
||||
description: Update a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/status:
|
||||
get:
|
||||
operationId: '%2Fapi%2Fstatus#0'
|
||||
|
@ -33323,4 +33531,5 @@ tags:
|
|||
name: Security Timeline API
|
||||
- description: SLO APIs enable you to define, manage and track service-level objectives
|
||||
name: slo
|
||||
- name: spaces
|
||||
- name: system
|
||||
|
|
|
@ -9860,6 +9860,214 @@ paths:
|
|||
-X POST api/saved_objects/_import?createNewCopies=true
|
||||
-H "kbn-xsrf: true"
|
||||
--form file=@file.ndjson
|
||||
/api/spaces/space:
|
||||
get:
|
||||
description: Get all spaces
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: query
|
||||
name: purpose
|
||||
required: false
|
||||
schema:
|
||||
enum:
|
||||
- any
|
||||
- copySavedObjectsIntoSpace
|
||||
- shareSavedObjectsIntoSpace
|
||||
type: string
|
||||
- in: query
|
||||
name: include_authorized_purposes
|
||||
required: true
|
||||
schema:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- enum:
|
||||
- false
|
||||
type: boolean
|
||||
x-oas-optional: true
|
||||
- type: boolean
|
||||
x-oas-optional: true
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
post:
|
||||
description: Create a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/spaces/space/{id}:
|
||||
delete:
|
||||
description: Delete a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
get:
|
||||
description: Get a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
put:
|
||||
description: Update a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/status:
|
||||
get:
|
||||
operationId: '%2Fapi%2Fstatus#0'
|
||||
|
@ -16476,4 +16684,5 @@ tags:
|
|||
x-displayName: Saved objects
|
||||
- description: SLO APIs enable you to define, manage and track service-level objectives
|
||||
name: slo
|
||||
- name: spaces
|
||||
- name: system
|
||||
|
|
|
@ -20330,6 +20330,949 @@ paths:
|
|||
tags:
|
||||
- Security AI Assistant API
|
||||
- Prompts API
|
||||
/api/security/role:
|
||||
get:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole#0'
|
||||
parameters: []
|
||||
responses: {}
|
||||
summary: Get all roles
|
||||
tags:
|
||||
- roles
|
||||
/api/security/role/{name}:
|
||||
delete:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: name
|
||||
required: true
|
||||
schema:
|
||||
minLength: 1
|
||||
type: string
|
||||
responses: {}
|
||||
summary: Delete a role
|
||||
tags:
|
||||
- roles
|
||||
get:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: path
|
||||
name: name
|
||||
required: true
|
||||
schema:
|
||||
minLength: 1
|
||||
type: string
|
||||
responses: {}
|
||||
summary: Get a role
|
||||
tags:
|
||||
- roles
|
||||
put:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: name
|
||||
required: true
|
||||
schema:
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
type: string
|
||||
- in: query
|
||||
name: createOnly
|
||||
required: false
|
||||
schema:
|
||||
default: false
|
||||
type: boolean
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
description:
|
||||
maxLength: 2048
|
||||
type: string
|
||||
elasticsearch:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
cluster:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
remote_cluster:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
required:
|
||||
- privileges
|
||||
- clusters
|
||||
type: array
|
||||
remote_indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- clusters
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
run_as:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kibana:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
base:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
feature:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
spaces:
|
||||
anyOf:
|
||||
- items:
|
||||
enum:
|
||||
- '*'
|
||||
type: string
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
default:
|
||||
- '*'
|
||||
required:
|
||||
- base
|
||||
type: array
|
||||
metadata:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
required:
|
||||
- elasticsearch
|
||||
responses: {}
|
||||
summary: Create or update a role
|
||||
tags:
|
||||
- roles
|
||||
/api/security/roles:
|
||||
post:
|
||||
operationId: '%2Fapi%2Fsecurity%2Froles#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
roles:
|
||||
additionalProperties:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
description:
|
||||
maxLength: 2048
|
||||
type: string
|
||||
elasticsearch:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
cluster:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
remote_cluster:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
required:
|
||||
- privileges
|
||||
- clusters
|
||||
type: array
|
||||
remote_indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- clusters
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
run_as:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kibana:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
base:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
feature:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
spaces:
|
||||
anyOf:
|
||||
- items:
|
||||
enum:
|
||||
- '*'
|
||||
type: string
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
default:
|
||||
- '*'
|
||||
required:
|
||||
- base
|
||||
type: array
|
||||
metadata:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
required:
|
||||
- elasticsearch
|
||||
type: object
|
||||
required:
|
||||
- roles
|
||||
responses: {}
|
||||
summary: Create or update roles
|
||||
tags:
|
||||
- roles
|
||||
/api/spaces/_copy_saved_objects:
|
||||
post:
|
||||
description: Copy saved objects to spaces
|
||||
operationId: '%2Fapi%2Fspaces%2F_copy_saved_objects#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
compatibilityMode:
|
||||
default: false
|
||||
type: boolean
|
||||
createNewCopies:
|
||||
default: true
|
||||
type: boolean
|
||||
includeReferences:
|
||||
default: false
|
||||
type: boolean
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
overwrite:
|
||||
default: false
|
||||
type: boolean
|
||||
spaces:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- spaces
|
||||
- objects
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_disable_legacy_url_aliases:
|
||||
post:
|
||||
description: Disable legacy URL aliases
|
||||
operationId: '%2Fapi%2Fspaces%2F_disable_legacy_url_aliases#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
aliases:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
sourceId:
|
||||
type: string
|
||||
targetSpace:
|
||||
type: string
|
||||
targetType:
|
||||
type: string
|
||||
required:
|
||||
- targetSpace
|
||||
- targetType
|
||||
- sourceId
|
||||
type: array
|
||||
required:
|
||||
- aliases
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_get_shareable_references:
|
||||
post:
|
||||
description: Get shareable references
|
||||
operationId: '%2Fapi%2Fspaces%2F_get_shareable_references#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
required:
|
||||
- objects
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_resolve_copy_saved_objects_errors:
|
||||
post:
|
||||
description: Resolve conflicts copying saved objects
|
||||
operationId: '%2Fapi%2Fspaces%2F_resolve_copy_saved_objects_errors#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
compatibilityMode:
|
||||
default: false
|
||||
type: boolean
|
||||
createNewCopies:
|
||||
default: true
|
||||
type: boolean
|
||||
includeReferences:
|
||||
default: false
|
||||
type: boolean
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
retries:
|
||||
additionalProperties:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
createNewCopy:
|
||||
type: boolean
|
||||
destinationId:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
ignoreMissingReferences:
|
||||
type: boolean
|
||||
overwrite:
|
||||
default: false
|
||||
type: boolean
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- retries
|
||||
- objects
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_update_objects_spaces:
|
||||
post:
|
||||
description: Update saved objects in spaces
|
||||
operationId: '%2Fapi%2Fspaces%2F_update_objects_spaces#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
spacesToAdd:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
spacesToRemove:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- objects
|
||||
- spacesToAdd
|
||||
- spacesToRemove
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/space:
|
||||
get:
|
||||
description: Get all spaces
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: query
|
||||
name: purpose
|
||||
required: false
|
||||
schema:
|
||||
enum:
|
||||
- any
|
||||
- copySavedObjectsIntoSpace
|
||||
- shareSavedObjectsIntoSpace
|
||||
type: string
|
||||
- in: query
|
||||
name: include_authorized_purposes
|
||||
required: true
|
||||
schema:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- enum:
|
||||
- false
|
||||
type: boolean
|
||||
x-oas-optional: true
|
||||
- type: boolean
|
||||
x-oas-optional: true
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
post:
|
||||
description: Create a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
solution:
|
||||
enum:
|
||||
- security
|
||||
- oblt
|
||||
- es
|
||||
- classic
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/spaces/space/{id}:
|
||||
delete:
|
||||
description: Delete a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
get:
|
||||
description: Get a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
put:
|
||||
description: Update a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
solution:
|
||||
enum:
|
||||
- security
|
||||
- oblt
|
||||
- es
|
||||
- classic
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/status:
|
||||
get:
|
||||
operationId: '%2Fapi%2Fstatus#0'
|
||||
|
@ -41270,6 +42213,7 @@ tags:
|
|||
- name: Fleet uninstall tokens
|
||||
- description: Machine learning
|
||||
name: ml
|
||||
- name: roles
|
||||
- description: >
|
||||
Export sets of saved objects that you want to import into {kib}, resolve
|
||||
import errors, and rotate an encryption key for encrypted saved objects
|
||||
|
@ -41325,4 +42269,5 @@ tags:
|
|||
name: Security Timeline API
|
||||
- description: SLO APIs enable you to define, manage and track service-level objectives
|
||||
name: slo
|
||||
- name: spaces
|
||||
- name: system
|
||||
|
|
|
@ -13132,6 +13132,949 @@ paths:
|
|||
summary: Resolve a saved object
|
||||
tags:
|
||||
- saved objects
|
||||
/api/security/role:
|
||||
get:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole#0'
|
||||
parameters: []
|
||||
responses: {}
|
||||
summary: Get all roles
|
||||
tags:
|
||||
- roles
|
||||
/api/security/role/{name}:
|
||||
delete:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: name
|
||||
required: true
|
||||
schema:
|
||||
minLength: 1
|
||||
type: string
|
||||
responses: {}
|
||||
summary: Delete a role
|
||||
tags:
|
||||
- roles
|
||||
get:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: path
|
||||
name: name
|
||||
required: true
|
||||
schema:
|
||||
minLength: 1
|
||||
type: string
|
||||
responses: {}
|
||||
summary: Get a role
|
||||
tags:
|
||||
- roles
|
||||
put:
|
||||
operationId: '%2Fapi%2Fsecurity%2Frole%2F%7Bname%7D#2'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: name
|
||||
required: true
|
||||
schema:
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
type: string
|
||||
- in: query
|
||||
name: createOnly
|
||||
required: false
|
||||
schema:
|
||||
default: false
|
||||
type: boolean
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
description:
|
||||
maxLength: 2048
|
||||
type: string
|
||||
elasticsearch:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
cluster:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
remote_cluster:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
required:
|
||||
- privileges
|
||||
- clusters
|
||||
type: array
|
||||
remote_indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- clusters
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
run_as:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kibana:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
base:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
feature:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
spaces:
|
||||
anyOf:
|
||||
- items:
|
||||
enum:
|
||||
- '*'
|
||||
type: string
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
default:
|
||||
- '*'
|
||||
required:
|
||||
- base
|
||||
type: array
|
||||
metadata:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
required:
|
||||
- elasticsearch
|
||||
responses: {}
|
||||
summary: Create or update a role
|
||||
tags:
|
||||
- roles
|
||||
/api/security/roles:
|
||||
post:
|
||||
operationId: '%2Fapi%2Fsecurity%2Froles#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
roles:
|
||||
additionalProperties:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
description:
|
||||
maxLength: 2048
|
||||
type: string
|
||||
elasticsearch:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
cluster:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
remote_cluster:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
required:
|
||||
- privileges
|
||||
- clusters
|
||||
type: array
|
||||
remote_indices:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
allow_restricted_indices:
|
||||
type: boolean
|
||||
clusters:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
field_security:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
names:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
privileges:
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
required:
|
||||
- clusters
|
||||
- names
|
||||
- privileges
|
||||
type: array
|
||||
run_as:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kibana:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
base:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
feature:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
spaces:
|
||||
anyOf:
|
||||
- items:
|
||||
enum:
|
||||
- '*'
|
||||
type: string
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
type: array
|
||||
- items:
|
||||
type: string
|
||||
type: array
|
||||
default:
|
||||
- '*'
|
||||
required:
|
||||
- base
|
||||
type: array
|
||||
metadata:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
required:
|
||||
- elasticsearch
|
||||
type: object
|
||||
required:
|
||||
- roles
|
||||
responses: {}
|
||||
summary: Create or update roles
|
||||
tags:
|
||||
- roles
|
||||
/api/spaces/_copy_saved_objects:
|
||||
post:
|
||||
description: Copy saved objects to spaces
|
||||
operationId: '%2Fapi%2Fspaces%2F_copy_saved_objects#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
compatibilityMode:
|
||||
default: false
|
||||
type: boolean
|
||||
createNewCopies:
|
||||
default: true
|
||||
type: boolean
|
||||
includeReferences:
|
||||
default: false
|
||||
type: boolean
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
overwrite:
|
||||
default: false
|
||||
type: boolean
|
||||
spaces:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- spaces
|
||||
- objects
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_disable_legacy_url_aliases:
|
||||
post:
|
||||
description: Disable legacy URL aliases
|
||||
operationId: '%2Fapi%2Fspaces%2F_disable_legacy_url_aliases#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
aliases:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
sourceId:
|
||||
type: string
|
||||
targetSpace:
|
||||
type: string
|
||||
targetType:
|
||||
type: string
|
||||
required:
|
||||
- targetSpace
|
||||
- targetType
|
||||
- sourceId
|
||||
type: array
|
||||
required:
|
||||
- aliases
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_get_shareable_references:
|
||||
post:
|
||||
description: Get shareable references
|
||||
operationId: '%2Fapi%2Fspaces%2F_get_shareable_references#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
required:
|
||||
- objects
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_resolve_copy_saved_objects_errors:
|
||||
post:
|
||||
description: Resolve conflicts copying saved objects
|
||||
operationId: '%2Fapi%2Fspaces%2F_resolve_copy_saved_objects_errors#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
compatibilityMode:
|
||||
default: false
|
||||
type: boolean
|
||||
createNewCopies:
|
||||
default: true
|
||||
type: boolean
|
||||
includeReferences:
|
||||
default: false
|
||||
type: boolean
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
retries:
|
||||
additionalProperties:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
createNewCopy:
|
||||
type: boolean
|
||||
destinationId:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
ignoreMissingReferences:
|
||||
type: boolean
|
||||
overwrite:
|
||||
default: false
|
||||
type: boolean
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- retries
|
||||
- objects
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/_update_objects_spaces:
|
||||
post:
|
||||
description: Update saved objects in spaces
|
||||
operationId: '%2Fapi%2Fspaces%2F_update_objects_spaces#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
objects:
|
||||
items:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
type: array
|
||||
spacesToAdd:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
spacesToRemove:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- objects
|
||||
- spacesToAdd
|
||||
- spacesToRemove
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags: []
|
||||
/api/spaces/space:
|
||||
get:
|
||||
description: Get all spaces
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: query
|
||||
name: purpose
|
||||
required: false
|
||||
schema:
|
||||
enum:
|
||||
- any
|
||||
- copySavedObjectsIntoSpace
|
||||
- shareSavedObjectsIntoSpace
|
||||
type: string
|
||||
- in: query
|
||||
name: include_authorized_purposes
|
||||
required: true
|
||||
schema:
|
||||
anyOf:
|
||||
- items: {}
|
||||
type: array
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: object
|
||||
- type: string
|
||||
nullable: true
|
||||
oneOf:
|
||||
- enum:
|
||||
- false
|
||||
type: boolean
|
||||
x-oas-optional: true
|
||||
- type: boolean
|
||||
x-oas-optional: true
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
post:
|
||||
description: Create a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
solution:
|
||||
enum:
|
||||
- security
|
||||
- oblt
|
||||
- es
|
||||
- classic
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/spaces/space/{id}:
|
||||
delete:
|
||||
description: Delete a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#2'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
get:
|
||||
description: Get a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#0'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
put:
|
||||
description: Update a space
|
||||
operationId: '%2Fapi%2Fspaces%2Fspace%2F%7Bid%7D#1'
|
||||
parameters:
|
||||
- description: The version of the API to use
|
||||
in: header
|
||||
name: elastic-api-version
|
||||
schema:
|
||||
default: '2023-10-31'
|
||||
enum:
|
||||
- '2023-10-31'
|
||||
type: string
|
||||
- description: A required header to protect against CSRF attacks
|
||||
in: header
|
||||
name: kbn-xsrf
|
||||
required: true
|
||||
schema:
|
||||
example: 'true'
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json; Elastic-Api-Version=2023-10-31:
|
||||
schema:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
_reserved:
|
||||
type: boolean
|
||||
color:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
disabledFeatures:
|
||||
default: []
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageUrl:
|
||||
type: string
|
||||
initials:
|
||||
maxLength: 2
|
||||
type: string
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
solution:
|
||||
enum:
|
||||
- security
|
||||
- oblt
|
||||
- es
|
||||
- classic
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
responses: {}
|
||||
summary: ''
|
||||
tags:
|
||||
- spaces
|
||||
/api/status:
|
||||
get:
|
||||
operationId: '%2Fapi%2Fstatus#0'
|
||||
|
@ -23448,6 +24391,7 @@ tags:
|
|||
- name: Fleet uninstall tokens
|
||||
- description: Machine learning
|
||||
name: ml
|
||||
- name: roles
|
||||
- description: >
|
||||
Export sets of saved objects that you want to import into {kib}, resolve
|
||||
import errors, and rotate an encryption key for encrypted saved objects
|
||||
|
@ -23474,4 +24418,5 @@ tags:
|
|||
x-displayName: Saved objects
|
||||
- description: SLO APIs enable you to define, manage and track service-level objectives
|
||||
name: slo
|
||||
- name: spaces
|
||||
- name: system
|
||||
|
|
|
@ -322,7 +322,10 @@ export interface HttpFetchOptions extends HttpRequestInit {
|
|||
|
||||
context?: KibanaExecutionContext;
|
||||
|
||||
/** @experimental */
|
||||
/**
|
||||
* When defined, the API version string used to populate the ELASTIC_HTTP_VERSION_HEADER.
|
||||
* Defaults to undefined.
|
||||
*/
|
||||
version?: ApiVersion;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,15 +16,19 @@ export class Role {
|
|||
|
||||
public async create(name: string, role: any) {
|
||||
this.log.debug(`creating role ${name}`);
|
||||
const { data, status, statusText } = await this.kibanaServer.request({
|
||||
path: `/api/security/role/${name}`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
kibana: role.kibana,
|
||||
elasticsearch: role.elasticsearch,
|
||||
},
|
||||
retries: 0,
|
||||
});
|
||||
const { data, status, statusText } = await this.kibanaServer
|
||||
.request({
|
||||
path: `/api/security/role/${name}`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
kibana: role.kibana,
|
||||
elasticsearch: role.elasticsearch,
|
||||
},
|
||||
retries: 0,
|
||||
})
|
||||
.catch((e) => {
|
||||
throw new Error(util.inspect(e.axiosError.response, true));
|
||||
});
|
||||
if (status !== 204) {
|
||||
throw new Error(
|
||||
`Expected status code of 204, received ${status} ${statusText}: ${util.inspect(data)}`
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
"@kbn/test-subj-selector",
|
||||
"@kbn/ftr-common-functional-services",
|
||||
"@kbn/std",
|
||||
"@kbn/expect"
|
||||
"@kbn/expect",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -120,6 +120,11 @@ export async function runTests(log: ToolingLog, options: RunTestsOptions) {
|
|||
logsDir: options.logsDir,
|
||||
installDir: options.installDir,
|
||||
onEarlyExit,
|
||||
extraKbnOpts: [
|
||||
config.get('serverless')
|
||||
? '--server.versioned.versionResolution=newest'
|
||||
: '--server.versioned.versionResolution=oldest',
|
||||
],
|
||||
});
|
||||
|
||||
if (abortCtrl.signal.aborted) {
|
||||
|
|
|
@ -116,3 +116,14 @@ export const IMAGE_FILE_TYPES = ['image/svg+xml', 'image/jpeg', 'image/png', 'im
|
|||
* Prefix for API actions.
|
||||
*/
|
||||
export const API_OPERATION_PREFIX = 'api:';
|
||||
|
||||
/**
|
||||
* The API version numbers used with the versioned router.
|
||||
*/
|
||||
export const API_VERSIONS = {
|
||||
roles: {
|
||||
public: {
|
||||
v1: '2023-10-31',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -46,6 +46,7 @@ import { toMountPoint } from '@kbn/react-kibana-mount';
|
|||
import type { Cluster } from '@kbn/remote-clusters-plugin/public';
|
||||
import { REMOTE_CLUSTERS_PATH } from '@kbn/remote-clusters-plugin/public';
|
||||
import { KibanaPrivileges } from '@kbn/security-role-management-model';
|
||||
import { API_VERSIONS as SPACES_API_VERSIONS } from '@kbn/spaces-plugin/common';
|
||||
import type { Space, SpacesApiUi } from '@kbn/spaces-plugin/public';
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
|
||||
|
@ -272,7 +273,7 @@ function useRole(
|
|||
function useSpaces(http: HttpStart, fatalErrors: FatalErrorsSetup) {
|
||||
const [spaces, setSpaces] = useState<{ enabled: boolean; list: Space[] } | null>(null);
|
||||
useEffect(() => {
|
||||
http.get<Space[]>('/api/spaces/space').then(
|
||||
http.get<Space[]>('/api/spaces/space', { version: SPACES_API_VERSIONS.public.v1 }).then(
|
||||
(fetchedSpaces) => setSpaces({ enabled: true, list: fetchedSpaces }),
|
||||
(err: IHttpFetchError) => {
|
||||
// Spaces plugin can be disabled and hence this endpoint can be unavailable.
|
||||
|
|
|
@ -9,25 +9,31 @@ import type { HttpStart } from '@kbn/core/public';
|
|||
import type { BulkUpdatePayload, BulkUpdateRoleResponse } from '@kbn/security-plugin-types-public';
|
||||
|
||||
import type { Role, RoleIndexPrivilege, RoleRemoteIndexPrivilege } from '../../../common';
|
||||
import { API_VERSIONS } from '../../../common/constants';
|
||||
import { copyRole } from '../../../common/model';
|
||||
|
||||
const version = API_VERSIONS.roles.public.v1;
|
||||
|
||||
export class RolesAPIClient {
|
||||
constructor(private readonly http: HttpStart) {}
|
||||
|
||||
public getRoles = async () => {
|
||||
return await this.http.get<Role[]>('/api/security/role');
|
||||
return await this.http.get<Role[]>('/api/security/role', { version });
|
||||
};
|
||||
|
||||
public getRole = async (roleName: string) => {
|
||||
return await this.http.get<Role>(`/api/security/role/${encodeURIComponent(roleName)}`);
|
||||
return await this.http.get<Role>(`/api/security/role/${encodeURIComponent(roleName)}`, {
|
||||
version,
|
||||
});
|
||||
};
|
||||
|
||||
public deleteRole = async (roleName: string) => {
|
||||
await this.http.delete(`/api/security/role/${encodeURIComponent(roleName)}`);
|
||||
await this.http.delete(`/api/security/role/${encodeURIComponent(roleName)}`, { version });
|
||||
};
|
||||
|
||||
public saveRole = async ({ role, createOnly = false }: { role: Role; createOnly?: boolean }) => {
|
||||
await this.http.put(`/api/security/role/${encodeURIComponent(role.name)}`, {
|
||||
version,
|
||||
body: JSON.stringify(this.transformRoleForSave(copyRole(role))),
|
||||
query: { createOnly },
|
||||
});
|
||||
|
@ -37,6 +43,7 @@ export class RolesAPIClient {
|
|||
rolesUpdate,
|
||||
}: BulkUpdatePayload): Promise<BulkUpdateRoleResponse> => {
|
||||
return await this.http.post('/api/security/roles', {
|
||||
version,
|
||||
body: JSON.stringify({
|
||||
roles: Object.fromEntries(
|
||||
rolesUpdate.map((role) => [role.name, this.transformRoleForSave(copyRole(role))])
|
||||
|
|
|
@ -9,9 +9,11 @@ import Boom from '@hapi/boom';
|
|||
|
||||
import { kibanaResponseFactory } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import type { LicenseCheck } from '@kbn/licensing-plugin/server';
|
||||
|
||||
import { defineDeleteRolesRoutes } from './delete';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { routeDefinitionParamsMock } from '../../index.mock';
|
||||
|
||||
interface TestOptions {
|
||||
|
@ -28,6 +30,8 @@ describe('DELETE role', () => {
|
|||
) => {
|
||||
test(description, async () => {
|
||||
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
|
||||
const versionedRouterMock = mockRouteDefinitionParams.router
|
||||
.versioned as MockedVersionedRouter;
|
||||
const mockCoreContext = coreMock.createRequestHandlerContext();
|
||||
const mockLicensingContext = {
|
||||
license: { check: jest.fn().mockReturnValue(licenseCheckResult) },
|
||||
|
@ -44,7 +48,9 @@ describe('DELETE role', () => {
|
|||
}
|
||||
|
||||
defineDeleteRolesRoutes(mockRouteDefinitionParams);
|
||||
const [[, handler]] = mockRouteDefinitionParams.router.delete.mock.calls;
|
||||
const handler = versionedRouterMock.getRoute('delete', '/api/security/role/{name}').versions[
|
||||
API_VERSIONS.roles.public.v1
|
||||
].handler;
|
||||
|
||||
const headers = { authorization: 'foo' };
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
|
|
|
@ -8,32 +8,40 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import type { RouteDefinitionParams } from '../..';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { wrapIntoCustomErrorResponse } from '../../../errors';
|
||||
import { createLicensedRouteHandler } from '../../licensed_route_handler';
|
||||
|
||||
export function defineDeleteRolesRoutes({ router }: RouteDefinitionParams) {
|
||||
router.delete(
|
||||
{
|
||||
router.versioned
|
||||
.delete({
|
||||
path: '/api/security/role/{name}',
|
||||
access: 'public',
|
||||
summary: `Delete a role`,
|
||||
options: {
|
||||
access: 'public',
|
||||
summary: `Delete a role`,
|
||||
tags: ['oas-tag:roles'],
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({ name: schema.string({ minLength: 1 }) }),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
await esClient.asCurrentUser.security.deleteRole({
|
||||
name: request.params.name,
|
||||
});
|
||||
|
||||
return response.noContent();
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.roles.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
params: schema.object({ name: schema.string({ minLength: 1 }) }),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
await esClient.asCurrentUser.security.deleteRole({
|
||||
name: request.params.name,
|
||||
});
|
||||
|
||||
return response.noContent();
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,11 @@ import Boom from '@hapi/boom';
|
|||
|
||||
import { kibanaResponseFactory } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import type { LicenseCheck } from '@kbn/licensing-plugin/server';
|
||||
|
||||
import { defineGetRolesRoutes } from './get';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { routeDefinitionParamsMock } from '../../index.mock';
|
||||
|
||||
const application = 'kibana-.kibana';
|
||||
|
@ -31,6 +33,8 @@ describe('GET role', () => {
|
|||
) => {
|
||||
test(description, async () => {
|
||||
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
|
||||
const versionedRouterMock = mockRouteDefinitionParams.router
|
||||
.versioned as MockedVersionedRouter;
|
||||
mockRouteDefinitionParams.authz.applicationName = application;
|
||||
mockRouteDefinitionParams.getFeatures = jest.fn().mockResolvedValue([]);
|
||||
|
||||
|
@ -50,7 +54,9 @@ describe('GET role', () => {
|
|||
}
|
||||
|
||||
defineGetRolesRoutes(mockRouteDefinitionParams);
|
||||
const [[, handler]] = mockRouteDefinitionParams.router.get.mock.calls;
|
||||
const handler = versionedRouterMock.getRoute('get', '/api/security/role/{name}').versions[
|
||||
API_VERSIONS.roles.public.v1
|
||||
].handler;
|
||||
|
||||
const headers = { authorization: 'foo' };
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import type { RouteDefinitionParams } from '../..';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { transformElasticsearchRoleToRole } from '../../../authorization';
|
||||
import { wrapIntoCustomErrorResponse } from '../../../errors';
|
||||
import { createLicensedRouteHandler } from '../../licensed_route_handler';
|
||||
|
@ -18,47 +19,54 @@ export function defineGetRolesRoutes({
|
|||
getFeatures,
|
||||
logger,
|
||||
}: RouteDefinitionParams) {
|
||||
router.get(
|
||||
{
|
||||
router.versioned
|
||||
.get({
|
||||
path: '/api/security/role/{name}',
|
||||
access: 'public',
|
||||
summary: `Get a role`,
|
||||
options: {
|
||||
access: 'public',
|
||||
summary: `Get a role`,
|
||||
tags: ['oas-tag:roles'],
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({ name: schema.string({ minLength: 1 }) }),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
|
||||
const [features, elasticsearchRoles] = await Promise.all([
|
||||
getFeatures(),
|
||||
await esClient.asCurrentUser.security.getRole({
|
||||
name: request.params.name,
|
||||
}),
|
||||
]);
|
||||
|
||||
const elasticsearchRole = elasticsearchRoles[request.params.name];
|
||||
|
||||
if (elasticsearchRole) {
|
||||
return response.ok({
|
||||
body: transformElasticsearchRoleToRole(
|
||||
features,
|
||||
// @ts-expect-error `SecurityIndicesPrivileges.names` expected to be `string[]`
|
||||
elasticsearchRole,
|
||||
request.params.name,
|
||||
authz.applicationName,
|
||||
logger
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return response.notFound();
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.roles.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
params: schema.object({ name: schema.string({ minLength: 1 }) }),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
|
||||
const [features, elasticsearchRoles] = await Promise.all([
|
||||
getFeatures(),
|
||||
await esClient.asCurrentUser.security.getRole({
|
||||
name: request.params.name,
|
||||
}),
|
||||
]);
|
||||
|
||||
const elasticsearchRole = elasticsearchRoles[request.params.name];
|
||||
|
||||
if (elasticsearchRole) {
|
||||
return response.ok({
|
||||
body: transformElasticsearchRoleToRole(
|
||||
features,
|
||||
// @ts-expect-error `SecurityIndicesPrivileges.names` expected to be `string[]`
|
||||
elasticsearchRole,
|
||||
request.params.name,
|
||||
authz.applicationName,
|
||||
logger
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return response.notFound();
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,11 @@ import Boom from '@hapi/boom';
|
|||
|
||||
import { kibanaResponseFactory } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import type { LicenseCheck } from '@kbn/licensing-plugin/server';
|
||||
|
||||
import { defineGetAllRolesRoutes } from './get_all';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { routeDefinitionParamsMock } from '../../index.mock';
|
||||
|
||||
const application = 'kibana-.kibana';
|
||||
|
@ -31,6 +33,8 @@ describe('GET all roles', () => {
|
|||
) => {
|
||||
test(description, async () => {
|
||||
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
|
||||
const versionedRouterMock = mockRouteDefinitionParams.router
|
||||
.versioned as MockedVersionedRouter;
|
||||
mockRouteDefinitionParams.authz.applicationName = application;
|
||||
mockRouteDefinitionParams.getFeatures = jest.fn().mockResolvedValue([]);
|
||||
|
||||
|
@ -50,7 +54,9 @@ describe('GET all roles', () => {
|
|||
}
|
||||
|
||||
defineGetAllRolesRoutes(mockRouteDefinitionParams);
|
||||
const [[, handler]] = mockRouteDefinitionParams.router.get.mock.calls;
|
||||
const handler = versionedRouterMock.getRoute('get', '/api/security/role').versions[
|
||||
API_VERSIONS.roles.public.v1
|
||||
].handler;
|
||||
|
||||
const headers = { authorization: 'foo' };
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { RouteDefinitionParams } from '../..';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { compareRolesByName, transformElasticsearchRoleToRole } from '../../../authorization';
|
||||
import { wrapIntoCustomErrorResponse } from '../../../errors';
|
||||
import { createLicensedRouteHandler } from '../../licensed_route_handler';
|
||||
|
@ -18,45 +19,50 @@ export function defineGetAllRolesRoutes({
|
|||
buildFlavor,
|
||||
config,
|
||||
}: RouteDefinitionParams) {
|
||||
router.get(
|
||||
{
|
||||
router.versioned
|
||||
.get({
|
||||
path: '/api/security/role',
|
||||
access: 'public',
|
||||
summary: `Get all roles`,
|
||||
options: {
|
||||
access: 'public',
|
||||
summary: `Get all roles`,
|
||||
tags: ['oas-tag:roles'],
|
||||
},
|
||||
validate: false,
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const hideReservedRoles = buildFlavor === 'serverless';
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
const [features, elasticsearchRoles] = await Promise.all([
|
||||
getFeatures(),
|
||||
await esClient.asCurrentUser.security.getRole(),
|
||||
]);
|
||||
|
||||
// Transform elasticsearch roles into Kibana roles and return in a list sorted by the role name.
|
||||
return response.ok({
|
||||
body: Object.entries(elasticsearchRoles)
|
||||
.map(([roleName, elasticsearchRole]) =>
|
||||
transformElasticsearchRoleToRole(
|
||||
features,
|
||||
// @ts-expect-error @elastic/elasticsearch SecurityIndicesPrivileges.names expected to be string[]
|
||||
elasticsearchRole,
|
||||
roleName,
|
||||
authz.applicationName,
|
||||
logger
|
||||
)
|
||||
)
|
||||
.filter((role) => {
|
||||
return !hideReservedRoles || !role.metadata?._reserved;
|
||||
})
|
||||
.sort(compareRolesByName),
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.roles.public.v1,
|
||||
validate: false,
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const hideReservedRoles = buildFlavor === 'serverless';
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
const [features, elasticsearchRoles] = await Promise.all([
|
||||
getFeatures(),
|
||||
await esClient.asCurrentUser.security.getRole(),
|
||||
]);
|
||||
|
||||
// Transform elasticsearch roles into Kibana roles and return in a list sorted by the role name.
|
||||
return response.ok({
|
||||
body: Object.entries(elasticsearchRoles)
|
||||
.map(([roleName, elasticsearchRole]) =>
|
||||
transformElasticsearchRoleToRole(
|
||||
features,
|
||||
// @ts-expect-error @elastic/elasticsearch SecurityIndicesPrivileges.names expected to be string[]
|
||||
elasticsearchRole,
|
||||
roleName,
|
||||
authz.applicationName,
|
||||
logger
|
||||
)
|
||||
)
|
||||
.filter((role) => {
|
||||
return !hideReservedRoles || !role.metadata?._reserved;
|
||||
})
|
||||
.sort(compareRolesByName),
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
|
||||
import { kibanaResponseFactory } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import { KibanaFeature } from '@kbn/features-plugin/server';
|
||||
import type { LicenseCheck } from '@kbn/licensing-plugin/server';
|
||||
import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server';
|
||||
|
||||
import type { BulkCreateOrUpdateRolesPayloadSchemaType } from './model/bulk_create_or_update_payload';
|
||||
import { defineBulkCreateOrUpdateRolesRoutes } from './post';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { securityFeatureUsageServiceMock } from '../../../feature_usage/index.mock';
|
||||
import { routeDefinitionParamsMock } from '../../index.mock';
|
||||
|
||||
|
@ -89,6 +91,7 @@ const postRolesTest = (
|
|||
) => {
|
||||
test(description, async () => {
|
||||
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
|
||||
const versionedRouterMock = mockRouteDefinitionParams.router.versioned as MockedVersionedRouter;
|
||||
mockRouteDefinitionParams.authz.applicationName = application;
|
||||
mockRouteDefinitionParams.authz.privileges.get.mockReturnValue(privilegeMap);
|
||||
|
||||
|
@ -158,13 +161,15 @@ const postRolesTest = (
|
|||
);
|
||||
|
||||
defineBulkCreateOrUpdateRolesRoutes(mockRouteDefinitionParams);
|
||||
const [[{ validate }, handler]] = mockRouteDefinitionParams.router.post.mock.calls;
|
||||
const { handler, config } = versionedRouterMock.getRoute('post', '/api/security/roles')
|
||||
.versions[API_VERSIONS.roles.public.v1];
|
||||
|
||||
const headers = { authorization: 'foo' };
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
method: 'post',
|
||||
path: '/api/security/roles',
|
||||
body: payload !== undefined ? (validate as any).body.validate(payload) : undefined,
|
||||
body:
|
||||
payload !== undefined ? (config.validate as any).request.body.validate(payload) : undefined,
|
||||
headers,
|
||||
});
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
transformPutPayloadToElasticsearchRole,
|
||||
} from './model';
|
||||
import type { RouteDefinitionParams } from '../..';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { wrapIntoCustomErrorResponse } from '../../../errors';
|
||||
import { validateKibanaPrivileges } from '../../../lib';
|
||||
import { createLicensedRouteHandler } from '../../licensed_route_handler';
|
||||
|
@ -39,97 +40,104 @@ export function defineBulkCreateOrUpdateRolesRoutes({
|
|||
getFeatures,
|
||||
getFeatureUsageService,
|
||||
}: RouteDefinitionParams) {
|
||||
router.post(
|
||||
{
|
||||
router.versioned
|
||||
.post({
|
||||
path: '/api/security/roles',
|
||||
access: 'public',
|
||||
summary: 'Create or update roles',
|
||||
options: {
|
||||
access: 'public',
|
||||
summary: 'Create or update roles',
|
||||
tags: ['oas-tag:roles'],
|
||||
},
|
||||
validate: {
|
||||
body: getBulkCreateOrUpdatePayloadSchema(() => {
|
||||
const privileges = authz.privileges.get();
|
||||
return {
|
||||
global: Object.keys(privileges.global),
|
||||
space: Object.keys(privileges.space),
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
const features = await getFeatures();
|
||||
|
||||
const { roles } = request.body;
|
||||
const validatedRolesNames = [];
|
||||
const kibanaErrors: RolesErrorsDetails = {};
|
||||
|
||||
for (const [roleName, role] of Object.entries(roles)) {
|
||||
const { validationErrors } = validateKibanaPrivileges(features, role.kibana);
|
||||
|
||||
if (validationErrors.length) {
|
||||
kibanaErrors[roleName] = {
|
||||
type: 'kibana_privilege_validation_exception',
|
||||
reason: `Role cannot be updated due to validation errors: ${JSON.stringify(
|
||||
validationErrors
|
||||
)}`,
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
validatedRolesNames.push(roleName);
|
||||
}
|
||||
|
||||
const rawRoles = await esClient.asCurrentUser.security.getRole(
|
||||
{ name: validatedRolesNames.join(',') },
|
||||
{ ignore: [404] }
|
||||
);
|
||||
|
||||
const esRolesPayload = Object.fromEntries(
|
||||
validatedRolesNames.map((roleName) => [
|
||||
roleName,
|
||||
transformPutPayloadToElasticsearchRole(
|
||||
roles[roleName],
|
||||
authz.applicationName,
|
||||
rawRoles[roleName] ? rawRoles[roleName].applications : []
|
||||
),
|
||||
])
|
||||
);
|
||||
|
||||
const esResponse = await esClient.asCurrentUser.transport.request<ESRolesResponse>({
|
||||
method: 'POST',
|
||||
path: '/_security/role',
|
||||
body: { roles: esRolesPayload },
|
||||
});
|
||||
|
||||
for (const roleName of [
|
||||
...(esResponse.created ?? []),
|
||||
...(esResponse.updated ?? []),
|
||||
...(esResponse.noop ?? []),
|
||||
]) {
|
||||
if (roleGrantsSubFeaturePrivileges(features, roles[roleName])) {
|
||||
getFeatureUsageService().recordSubFeaturePrivilegeUsage();
|
||||
}
|
||||
}
|
||||
|
||||
const { created, noop, updated, errors: esErrors } = esResponse;
|
||||
const hasAnyErrors = Object.keys(kibanaErrors).length || esErrors?.count;
|
||||
|
||||
return response.ok({
|
||||
body: {
|
||||
created,
|
||||
noop,
|
||||
updated,
|
||||
...(hasAnyErrors && {
|
||||
errors: { ...kibanaErrors, ...(esErrors?.details ?? {}) },
|
||||
})
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.roles.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
body: getBulkCreateOrUpdatePayloadSchema(() => {
|
||||
const privileges = authz.privileges.get();
|
||||
return {
|
||||
global: Object.keys(privileges.global),
|
||||
space: Object.keys(privileges.space),
|
||||
};
|
||||
}),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
const features = await getFeatures();
|
||||
|
||||
const { roles } = request.body;
|
||||
const validatedRolesNames = [];
|
||||
const kibanaErrors: RolesErrorsDetails = {};
|
||||
|
||||
for (const [roleName, role] of Object.entries(roles)) {
|
||||
const { validationErrors } = validateKibanaPrivileges(features, role.kibana);
|
||||
|
||||
if (validationErrors.length) {
|
||||
kibanaErrors[roleName] = {
|
||||
type: 'kibana_privilege_validation_exception',
|
||||
reason: `Role cannot be updated due to validation errors: ${JSON.stringify(
|
||||
validationErrors
|
||||
)}`,
|
||||
};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
validatedRolesNames.push(roleName);
|
||||
}
|
||||
|
||||
const rawRoles = await esClient.asCurrentUser.security.getRole(
|
||||
{ name: validatedRolesNames.join(',') },
|
||||
{ ignore: [404] }
|
||||
);
|
||||
|
||||
const esRolesPayload = Object.fromEntries(
|
||||
validatedRolesNames.map((roleName) => [
|
||||
roleName,
|
||||
transformPutPayloadToElasticsearchRole(
|
||||
roles[roleName],
|
||||
authz.applicationName,
|
||||
rawRoles[roleName] ? rawRoles[roleName].applications : []
|
||||
),
|
||||
])
|
||||
);
|
||||
|
||||
const esResponse = await esClient.asCurrentUser.transport.request<ESRolesResponse>({
|
||||
method: 'POST',
|
||||
path: '/_security/role',
|
||||
body: { roles: esRolesPayload },
|
||||
});
|
||||
|
||||
for (const roleName of [
|
||||
...(esResponse.created ?? []),
|
||||
...(esResponse.updated ?? []),
|
||||
...(esResponse.noop ?? []),
|
||||
]) {
|
||||
if (roleGrantsSubFeaturePrivileges(features, roles[roleName])) {
|
||||
getFeatureUsageService().recordSubFeaturePrivilegeUsage();
|
||||
}
|
||||
}
|
||||
|
||||
const { created, noop, updated, errors: esErrors } = esResponse;
|
||||
const hasAnyErrors = Object.keys(kibanaErrors).length || esErrors?.count;
|
||||
|
||||
return response.ok({
|
||||
body: {
|
||||
created,
|
||||
noop,
|
||||
updated,
|
||||
...(hasAnyErrors && {
|
||||
errors: { ...kibanaErrors, ...(esErrors?.details ?? {}) },
|
||||
}),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,11 +8,13 @@
|
|||
import type { Type } from '@kbn/config-schema';
|
||||
import { kibanaResponseFactory } from '@kbn/core/server';
|
||||
import { coreMock, httpServerMock } from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import { KibanaFeature } from '@kbn/features-plugin/server';
|
||||
import type { LicenseCheck } from '@kbn/licensing-plugin/server';
|
||||
import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server';
|
||||
|
||||
import { definePutRolesRoutes } from './put';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { securityFeatureUsageServiceMock } from '../../../feature_usage/index.mock';
|
||||
import { routeDefinitionParamsMock } from '../../index.mock';
|
||||
|
||||
|
@ -74,6 +76,7 @@ const putRoleTest = (
|
|||
) => {
|
||||
test(description, async () => {
|
||||
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
|
||||
const versionedRouterMock = mockRouteDefinitionParams.router.versioned as MockedVersionedRouter;
|
||||
mockRouteDefinitionParams.authz.applicationName = application;
|
||||
mockRouteDefinitionParams.authz.privileges.get.mockReturnValue(privilegeMap);
|
||||
|
||||
|
@ -143,7 +146,8 @@ const putRoleTest = (
|
|||
);
|
||||
|
||||
definePutRolesRoutes(mockRouteDefinitionParams);
|
||||
const [[{ validate }, handler]] = mockRouteDefinitionParams.router.put.mock.calls;
|
||||
const { handler, config } = versionedRouterMock.getRoute('put', '/api/security/role/{name}')
|
||||
.versions[API_VERSIONS.roles.public.v1];
|
||||
|
||||
const headers = { authorization: 'foo' };
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
|
@ -151,7 +155,8 @@ const putRoleTest = (
|
|||
path: `/api/security/role/${name}`,
|
||||
query: { createOnly },
|
||||
params: { name },
|
||||
body: payload !== undefined ? (validate as any).body.validate(payload) : undefined,
|
||||
body:
|
||||
payload !== undefined ? (config.validate as any).request.body.validate(payload) : undefined,
|
||||
headers,
|
||||
});
|
||||
|
||||
|
@ -188,11 +193,15 @@ describe('PUT role', () => {
|
|||
let requestParamsSchema: Type<any>;
|
||||
beforeEach(() => {
|
||||
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
|
||||
const versionedRouterMock = mockRouteDefinitionParams.router
|
||||
.versioned as MockedVersionedRouter;
|
||||
mockRouteDefinitionParams.authz.privileges.get.mockReturnValue(privilegeMap);
|
||||
definePutRolesRoutes(mockRouteDefinitionParams);
|
||||
|
||||
const [[{ validate }]] = mockRouteDefinitionParams.router.put.mock.calls;
|
||||
requestParamsSchema = (validate as any).params;
|
||||
const { config } = versionedRouterMock.getRoute('put', '/api/security/role/{name}').versions[
|
||||
API_VERSIONS.roles.public.v1
|
||||
];
|
||||
requestParamsSchema = (config.validate as any).request.params;
|
||||
});
|
||||
|
||||
test('requires name in params', () => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import { schema } from '@kbn/config-schema';
|
|||
import { roleGrantsSubFeaturePrivileges } from './lib';
|
||||
import { getPutPayloadSchema, transformPutPayloadToElasticsearchRole } from './model';
|
||||
import type { RouteDefinitionParams } from '../..';
|
||||
import { API_VERSIONS } from '../../../../common/constants';
|
||||
import { wrapIntoCustomErrorResponse } from '../../../errors';
|
||||
import { validateKibanaPrivileges } from '../../../lib';
|
||||
import { createLicensedRouteHandler } from '../../licensed_route_handler';
|
||||
|
@ -20,75 +21,85 @@ export function definePutRolesRoutes({
|
|||
getFeatures,
|
||||
getFeatureUsageService,
|
||||
}: RouteDefinitionParams) {
|
||||
router.put(
|
||||
{
|
||||
router.versioned
|
||||
.put({
|
||||
path: '/api/security/role/{name}',
|
||||
access: 'public',
|
||||
summary: `Create or update a role`,
|
||||
options: {
|
||||
access: 'public',
|
||||
summary: `Create or update a role`,
|
||||
tags: ['oas-tag:roles'],
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({ name: schema.string({ minLength: 1, maxLength: 1024 }) }),
|
||||
query: schema.object({ createOnly: schema.boolean({ defaultValue: false }) }),
|
||||
body: getPutPayloadSchema(() => {
|
||||
const privileges = authz.privileges.get();
|
||||
return {
|
||||
global: Object.keys(privileges.global),
|
||||
space: Object.keys(privileges.space),
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const { name } = request.params;
|
||||
const { createOnly } = request.query;
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
|
||||
const [features, rawRoles] = await Promise.all([
|
||||
getFeatures(),
|
||||
esClient.asCurrentUser.security.getRole({ name: request.params.name }, { ignore: [404] }),
|
||||
]);
|
||||
|
||||
const { validationErrors } = validateKibanaPrivileges(features, request.body.kibana);
|
||||
|
||||
if (validationErrors.length) {
|
||||
return response.badRequest({
|
||||
body: {
|
||||
message: `Role cannot be updated due to validation errors: ${JSON.stringify(
|
||||
validationErrors
|
||||
)}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (createOnly && !!rawRoles[name]) {
|
||||
return response.conflict({
|
||||
body: {
|
||||
message: `Role already exists and cannot be created: ${name}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const body = transformPutPayloadToElasticsearchRole(
|
||||
request.body,
|
||||
authz.applicationName,
|
||||
rawRoles[name] ? rawRoles[name].applications : []
|
||||
);
|
||||
|
||||
await esClient.asCurrentUser.security.putRole({
|
||||
name: request.params.name,
|
||||
body,
|
||||
});
|
||||
|
||||
if (roleGrantsSubFeaturePrivileges(features, request.body)) {
|
||||
getFeatureUsageService().recordSubFeaturePrivilegeUsage();
|
||||
}
|
||||
|
||||
return response.noContent();
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.roles.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
params: schema.object({ name: schema.string({ minLength: 1, maxLength: 1024 }) }),
|
||||
query: schema.object({ createOnly: schema.boolean({ defaultValue: false }) }),
|
||||
body: getPutPayloadSchema(() => {
|
||||
const privileges = authz.privileges.get();
|
||||
return {
|
||||
global: Object.keys(privileges.global),
|
||||
space: Object.keys(privileges.space),
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const { name } = request.params;
|
||||
const { createOnly } = request.query;
|
||||
try {
|
||||
const esClient = (await context.core).elasticsearch.client;
|
||||
|
||||
const [features, rawRoles] = await Promise.all([
|
||||
getFeatures(),
|
||||
esClient.asCurrentUser.security.getRole(
|
||||
{ name: request.params.name },
|
||||
{ ignore: [404] }
|
||||
),
|
||||
]);
|
||||
|
||||
const { validationErrors } = validateKibanaPrivileges(features, request.body.kibana);
|
||||
|
||||
if (validationErrors.length) {
|
||||
return response.badRequest({
|
||||
body: {
|
||||
message: `Role cannot be updated due to validation errors: ${JSON.stringify(
|
||||
validationErrors
|
||||
)}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (createOnly && !!rawRoles[name]) {
|
||||
return response.conflict({
|
||||
body: {
|
||||
message: `Role already exists and cannot be created: ${name}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const body = transformPutPayloadToElasticsearchRole(
|
||||
request.body,
|
||||
authz.applicationName,
|
||||
rawRoles[name] ? rawRoles[name].applications : []
|
||||
);
|
||||
|
||||
await esClient.asCurrentUser.security.putRole({
|
||||
name: request.params.name,
|
||||
body,
|
||||
});
|
||||
|
||||
if (roleGrantsSubFeaturePrivileges(features, request.body)) {
|
||||
getFeatureUsageService().recordSubFeaturePrivilegeUsage();
|
||||
}
|
||||
|
||||
return response.noContent();
|
||||
} catch (error) {
|
||||
return response.customError(wrapIntoCustomErrorResponse(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
"@kbn/security-authorization-core",
|
||||
"@kbn/security-role-management-model",
|
||||
"@kbn/security-ui-components",
|
||||
"@kbn/core-http-router-server-mocks",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -43,3 +43,12 @@ export const SOLUTION_VIEW_CLASSIC = 'classic' as const;
|
|||
export const FEATURE_PRIVILEGES_ALL = 'all' as const;
|
||||
export const FEATURE_PRIVILEGES_READ = 'read' as const;
|
||||
export const FEATURE_PRIVILEGES_CUSTOM = 'custom' as const;
|
||||
|
||||
/**
|
||||
* The API version numbers used with the versioned router.
|
||||
*/
|
||||
export const API_VERSIONS = {
|
||||
public: {
|
||||
v1: '2023-10-31',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ export {
|
|||
SPACE_SEARCH_COUNT_THRESHOLD,
|
||||
ENTER_SPACE_PATH,
|
||||
DEFAULT_SPACE_ID,
|
||||
API_VERSIONS,
|
||||
} from './constants';
|
||||
export { addSpaceIdToPath, getSpaceIdFromPath } from './lib/spaces_url_parser';
|
||||
export type {
|
||||
|
|
|
@ -13,7 +13,12 @@ import type { SavedObjectsCollectMultiNamespaceReferencesResponse } from '@kbn/c
|
|||
import type { LegacyUrlAliasTarget } from '@kbn/core-saved-objects-common';
|
||||
import type { Role } from '@kbn/security-plugin-types-common';
|
||||
|
||||
import type { GetAllSpacesOptions, GetSpaceResult, Space } from '../../common';
|
||||
import {
|
||||
API_VERSIONS,
|
||||
type GetAllSpacesOptions,
|
||||
type GetSpaceResult,
|
||||
type Space,
|
||||
} from '../../common';
|
||||
import type { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types';
|
||||
import type { SpaceContentTypeSummaryItem } from '../types';
|
||||
|
||||
|
@ -23,6 +28,7 @@ interface SavedObjectTarget {
|
|||
}
|
||||
|
||||
const TAG_TYPE = 'tag';
|
||||
const version = API_VERSIONS.public.v1;
|
||||
|
||||
export class SpacesManager {
|
||||
private activeSpace$: BehaviorSubject<Space | null> = new BehaviorSubject<Space | null>(null);
|
||||
|
@ -49,11 +55,11 @@ export class SpacesManager {
|
|||
public async getSpaces(options: GetAllSpacesOptions = {}): Promise<GetSpaceResult[]> {
|
||||
const { purpose, includeAuthorizedPurposes } = options;
|
||||
const query = { purpose, include_authorized_purposes: includeAuthorizedPurposes };
|
||||
return await this.http.get('/api/spaces/space', { query });
|
||||
return await this.http.get('/api/spaces/space', { query, version });
|
||||
}
|
||||
|
||||
public async getSpace(id: string): Promise<Space> {
|
||||
return await this.http.get(`/api/spaces/space/${encodeURIComponent(id)}`);
|
||||
return await this.http.get(`/api/spaces/space/${encodeURIComponent(id)}`, { version });
|
||||
}
|
||||
|
||||
public async getActiveSpace({ forceRefresh = false } = {}) {
|
||||
|
@ -69,6 +75,7 @@ export class SpacesManager {
|
|||
public async createSpace(space: Space) {
|
||||
await this.http.post(`/api/spaces/space`, {
|
||||
body: JSON.stringify(space),
|
||||
version,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -78,6 +85,7 @@ export class SpacesManager {
|
|||
overwrite: true,
|
||||
},
|
||||
body: JSON.stringify(space),
|
||||
version,
|
||||
});
|
||||
|
||||
const activeSpaceId = (await this.getActiveSpace()).id;
|
||||
|
@ -88,7 +96,7 @@ export class SpacesManager {
|
|||
}
|
||||
|
||||
public async deleteSpace(space: Space) {
|
||||
await this.http.delete(`/api/spaces/space/${encodeURIComponent(space.id)}`);
|
||||
await this.http.delete(`/api/spaces/space/${encodeURIComponent(space.id)}`, { version });
|
||||
}
|
||||
|
||||
public async disableLegacyUrlAliases(aliases: LegacyUrlAliasTarget[]) {
|
||||
|
|
|
@ -16,9 +16,11 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initDeleteSpacesApi } from './delete';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
import { SpacesClientService } from '../../../spaces_client';
|
||||
import { SpacesService } from '../../../spaces_service';
|
||||
|
@ -36,7 +38,7 @@ describe('Spaces Public API', () => {
|
|||
const setup = async () => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpService.createRouter();
|
||||
|
||||
const versionedRouterMock = router.versioned as MockedVersionedRouter;
|
||||
const savedObjectsRepositoryMock = createMockSavedObjectsRepository(spacesSavedObjects);
|
||||
|
||||
const log = loggingSystemMock.create().get('spaces');
|
||||
|
@ -71,10 +73,13 @@ describe('Spaces Public API', () => {
|
|||
isServerless: false,
|
||||
});
|
||||
|
||||
const [routeDefinition, routeHandler] = router.delete.mock.calls[0];
|
||||
const { handler: routeHandler, config } = versionedRouterMock.getRoute(
|
||||
'delete',
|
||||
'/api/spaces/space/{id}'
|
||||
).versions[API_VERSIONS.public.v1];
|
||||
|
||||
return {
|
||||
routeValidation: routeDefinition.validate as RouteValidatorConfig<{}, {}, {}>,
|
||||
routeValidation: (config.validate as any).request as RouteValidatorConfig<{}, {}, {}>,
|
||||
routeHandler,
|
||||
savedObjectsRepositoryMock,
|
||||
};
|
||||
|
|
|
@ -11,47 +11,55 @@ import { schema } from '@kbn/config-schema';
|
|||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
|
||||
import type { ExternalRouteDeps } from '.';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { wrapError } from '../../../lib/errors';
|
||||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initDeleteSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { router, log, getSpacesService, isServerless } = deps;
|
||||
const { router, log, getSpacesService } = deps;
|
||||
|
||||
router.delete(
|
||||
{
|
||||
router.versioned
|
||||
.delete({
|
||||
path: '/api/spaces/space/{id}',
|
||||
access: 'public',
|
||||
description: `Delete a space`,
|
||||
options: {
|
||||
access: isServerless ? 'internal' : 'public',
|
||||
description: `Delete a space`,
|
||||
tags: ['oas-tag:spaces'],
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
const id = request.params.id;
|
||||
|
||||
try {
|
||||
await spacesClient.delete(id);
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound();
|
||||
} else if (SavedObjectsErrorHelpers.isEsCannotExecuteScriptError(error)) {
|
||||
log.error(
|
||||
`Failed to delete space '${id}', cannot execute script in Elasticsearch query: ${error.message}`
|
||||
);
|
||||
return response.customError(
|
||||
wrapError(Boom.badRequest('Cannot execute script in Elasticsearch query'))
|
||||
);
|
||||
}
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
|
||||
return response.noContent();
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
const id = request.params.id;
|
||||
|
||||
try {
|
||||
await spacesClient.delete(id);
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound();
|
||||
} else if (SavedObjectsErrorHelpers.isEsCannotExecuteScriptError(error)) {
|
||||
log.error(
|
||||
`Failed to delete space '${id}', cannot execute script in Elasticsearch query: ${error.message}`
|
||||
);
|
||||
return response.customError(
|
||||
wrapError(Boom.badRequest('Cannot execute script in Elasticsearch query'))
|
||||
);
|
||||
}
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
|
||||
return response.noContent();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initGetSpaceApi } from './get';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
import { SpacesClientService } from '../../../spaces_client';
|
||||
import { SpacesService } from '../../../spaces_service';
|
||||
|
@ -35,6 +37,7 @@ describe('GET space', () => {
|
|||
const setup = async () => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpService.createRouter();
|
||||
const versionedRouterMock = router.versioned as MockedVersionedRouter;
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
|
@ -70,8 +73,12 @@ describe('GET space', () => {
|
|||
isServerless: false,
|
||||
});
|
||||
|
||||
const { handler } = versionedRouterMock.getRoute('get', '/api/spaces/space/{id}').versions[
|
||||
API_VERSIONS.public.v1
|
||||
];
|
||||
|
||||
return {
|
||||
routeHandler: router.get.mock.calls[0][1],
|
||||
routeHandler: handler,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -9,40 +9,47 @@ import { schema } from '@kbn/config-schema';
|
|||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
|
||||
import type { ExternalRouteDeps } from '.';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { wrapError } from '../../../lib/errors';
|
||||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initGetSpaceApi(deps: ExternalRouteDeps) {
|
||||
const { router, getSpacesService, isServerless } = deps;
|
||||
const { router, getSpacesService } = deps;
|
||||
|
||||
router.get(
|
||||
{
|
||||
router.versioned
|
||||
.get({
|
||||
path: '/api/spaces/space/{id}',
|
||||
access: 'public',
|
||||
description: `Get a space`,
|
||||
options: {
|
||||
access: isServerless ? 'internal' : 'public',
|
||||
description: `Get a space`,
|
||||
tags: ['oas-tag:spaces'],
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const spaceId = request.params.id;
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
try {
|
||||
const space = await spacesClient.get(spaceId);
|
||||
return response.ok({
|
||||
body: space,
|
||||
});
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound();
|
||||
}
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const spaceId = request.params.id;
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
try {
|
||||
const space = await spacesClient.get(spaceId);
|
||||
return response.ok({
|
||||
body: space,
|
||||
});
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound();
|
||||
}
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,10 +15,13 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import type { RouteValidatorConfig } from '@kbn/core-http-server';
|
||||
import { getRequestValidation } from '@kbn/core-http-server';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initGetAllSpacesApi } from './get_all';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
import { SpacesClientService } from '../../../spaces_client';
|
||||
import { SpacesService } from '../../../spaces_service';
|
||||
|
@ -37,6 +40,7 @@ describe('GET /spaces/space', () => {
|
|||
const setup = async () => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpService.createRouter();
|
||||
const versionedRouterMock = router.versioned as MockedVersionedRouter;
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
|
@ -72,9 +76,13 @@ describe('GET /spaces/space', () => {
|
|||
isServerless: false,
|
||||
});
|
||||
|
||||
const { handler, config } = versionedRouterMock.getRoute('get', '/api/spaces/space').versions[
|
||||
API_VERSIONS.public.v1
|
||||
];
|
||||
|
||||
return {
|
||||
routeConfig: router.get.mock.calls[0][0],
|
||||
routeHandler: router.get.mock.calls[0][1],
|
||||
routeValidation: (config.validate as any).request as RouteValidatorConfig<{}, {}, {}> | false,
|
||||
routeHandler: handler,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -92,17 +100,17 @@ describe('GET /spaces/space', () => {
|
|||
});
|
||||
|
||||
it(`returns expected result when specifying include_authorized_purposes=true`, async () => {
|
||||
const { routeConfig, routeHandler } = await setup();
|
||||
const { routeValidation, routeHandler } = await setup();
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
method: 'get',
|
||||
query: { purpose, include_authorized_purposes: true },
|
||||
});
|
||||
|
||||
if (routeConfig.validate === false) {
|
||||
if (routeValidation === false) {
|
||||
throw new Error('Test setup failure. Expected route validation');
|
||||
}
|
||||
const queryParamsValidation = getRequestValidation(routeConfig.validate)
|
||||
const queryParamsValidation = getRequestValidation(routeValidation)
|
||||
.query! as ObjectType<any>;
|
||||
|
||||
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
|
||||
|
|
|
@ -8,63 +8,70 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import type { ExternalRouteDeps } from '.';
|
||||
import type { Space } from '../../../../common';
|
||||
import { API_VERSIONS, type Space } from '../../../../common';
|
||||
import { wrapError } from '../../../lib/errors';
|
||||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initGetAllSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { router, log, getSpacesService, isServerless } = deps;
|
||||
const { router, log, getSpacesService } = deps;
|
||||
|
||||
router.get(
|
||||
{
|
||||
router.versioned
|
||||
.get({
|
||||
path: '/api/spaces/space',
|
||||
access: 'public',
|
||||
description: `Get all spaces`,
|
||||
options: {
|
||||
access: isServerless ? 'internal' : 'public',
|
||||
description: `Get all spaces`,
|
||||
tags: ['oas-tag:spaces'],
|
||||
},
|
||||
validate: {
|
||||
query: schema.object({
|
||||
purpose: schema.maybe(
|
||||
schema.oneOf([
|
||||
schema.literal('any'),
|
||||
schema.literal('copySavedObjectsIntoSpace'),
|
||||
schema.literal('shareSavedObjectsIntoSpace'),
|
||||
])
|
||||
),
|
||||
include_authorized_purposes: schema.conditional(
|
||||
schema.siblingRef('purpose'),
|
||||
schema.string(),
|
||||
schema.maybe(schema.literal(false)),
|
||||
schema.maybe(schema.boolean())
|
||||
),
|
||||
}),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
log.debug(`Inside GET /api/spaces/space`);
|
||||
|
||||
const { purpose, include_authorized_purposes: includeAuthorizedPurposes } = request.query;
|
||||
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
let spaces: Space[];
|
||||
|
||||
try {
|
||||
log.debug(
|
||||
`Attempting to retrieve all spaces for ${purpose} purpose with includeAuthorizedPurposes=${includeAuthorizedPurposes}`
|
||||
);
|
||||
spaces = await spacesClient.getAll({ purpose, includeAuthorizedPurposes });
|
||||
log.debug(
|
||||
`Retrieved ${spaces.length} spaces for ${purpose} purpose with includeAuthorizedPurposes=${includeAuthorizedPurposes}`
|
||||
);
|
||||
} catch (error) {
|
||||
log.debug(
|
||||
`Error retrieving spaces for ${purpose} purpose with includeAuthorizedPurposes=${includeAuthorizedPurposes}: ${error}`
|
||||
);
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
|
||||
return response.ok({ body: spaces });
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
query: schema.object({
|
||||
purpose: schema.maybe(
|
||||
schema.oneOf([
|
||||
schema.literal('any'),
|
||||
schema.literal('copySavedObjectsIntoSpace'),
|
||||
schema.literal('shareSavedObjectsIntoSpace'),
|
||||
])
|
||||
),
|
||||
include_authorized_purposes: schema.conditional(
|
||||
schema.siblingRef('purpose'),
|
||||
schema.string(),
|
||||
schema.maybe(schema.literal(false)),
|
||||
schema.maybe(schema.boolean())
|
||||
),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
log.debug(`Inside GET /api/spaces/space`);
|
||||
|
||||
const { purpose, include_authorized_purposes: includeAuthorizedPurposes } = request.query;
|
||||
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
let spaces: Space[];
|
||||
|
||||
try {
|
||||
log.debug(
|
||||
`Attempting to retrieve all spaces for ${purpose} purpose with includeAuthorizedPurposes=${includeAuthorizedPurposes}`
|
||||
);
|
||||
spaces = await spacesClient.getAll({ purpose, includeAuthorizedPurposes });
|
||||
log.debug(
|
||||
`Retrieved ${spaces.length} spaces for ${purpose} purpose with includeAuthorizedPurposes=${includeAuthorizedPurposes}`
|
||||
);
|
||||
} catch (error) {
|
||||
log.debug(
|
||||
`Error retrieving spaces for ${purpose} purpose with includeAuthorizedPurposes=${includeAuthorizedPurposes}: ${error}`
|
||||
);
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
|
||||
return response.ok({ body: spaces });
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,9 +16,11 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initPostSpacesApi } from './post';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
import { SpacesClientService } from '../../../spaces_client';
|
||||
import { SpacesService } from '../../../spaces_service';
|
||||
|
@ -36,6 +38,7 @@ describe('Spaces Public API', () => {
|
|||
const setup = async () => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpService.createRouter();
|
||||
const versionedRouterMock = router.versioned as MockedVersionedRouter;
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
|
@ -75,11 +78,13 @@ describe('Spaces Public API', () => {
|
|||
isServerless: false,
|
||||
});
|
||||
|
||||
const [routeDefinition, routeHandler] = router.post.mock.calls[0];
|
||||
const { handler, config } = versionedRouterMock.getRoute('post', '/api/spaces/space').versions[
|
||||
API_VERSIONS.public.v1
|
||||
];
|
||||
|
||||
return {
|
||||
routeValidation: routeDefinition.validate as RouteValidatorConfig<{}, {}, {}>,
|
||||
routeHandler,
|
||||
routeValidation: (config.validate as any).request as RouteValidatorConfig<{}, {}, {}>,
|
||||
routeHandler: handler,
|
||||
savedObjectsRepositoryMock,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ import Boom from '@hapi/boom';
|
|||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
|
||||
import type { ExternalRouteDeps } from '.';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { wrapError } from '../../../lib/errors';
|
||||
import { getSpaceSchema } from '../../../lib/space_schema';
|
||||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
@ -17,37 +18,42 @@ import { createLicensedRouteHandler } from '../../lib';
|
|||
export function initPostSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { router, log, getSpacesService, isServerless } = deps;
|
||||
|
||||
router.post(
|
||||
{
|
||||
router.versioned
|
||||
.post({
|
||||
path: '/api/spaces/space',
|
||||
access: 'public',
|
||||
description: `Create a space`,
|
||||
options: {
|
||||
access: isServerless ? 'internal' : 'public',
|
||||
description: `Create a space`,
|
||||
tags: ['oas-tag:spaces'],
|
||||
},
|
||||
validate: {
|
||||
body: getSpaceSchema(isServerless),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
log.debug(`Inside POST /api/spaces/space`);
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
const space = request.body;
|
||||
|
||||
try {
|
||||
log.debug(`Attempting to create space`);
|
||||
const createdSpace = await spacesClient.create(space);
|
||||
return response.ok({ body: createdSpace });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isConflictError(error)) {
|
||||
const { body } = wrapError(
|
||||
Boom.conflict(`A space with the identifier ${space.id} already exists.`)
|
||||
);
|
||||
return response.conflict({ body });
|
||||
}
|
||||
log.debug(`Error creating space: ${error}`);
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
body: getSpaceSchema(isServerless),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
log.debug(`Inside POST /api/spaces/space`);
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
const space = request.body;
|
||||
try {
|
||||
log.debug(`Attempting to create space`);
|
||||
const createdSpace = await spacesClient.create(space);
|
||||
return response.ok({ body: createdSpace });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isConflictError(error)) {
|
||||
const { body } = wrapError(
|
||||
Boom.conflict(`A space with the identifier ${space.id} already exists.`)
|
||||
);
|
||||
return response.conflict({ body });
|
||||
}
|
||||
log.debug(`Error creating space: ${error}`);
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,9 +16,11 @@ import {
|
|||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import type { MockedVersionedRouter } from '@kbn/core-http-router-server-mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import { initPutSpacesApi } from './put';
|
||||
import { API_VERSIONS } from '../../../../common';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
import { SpacesClientService } from '../../../spaces_client';
|
||||
import { SpacesService } from '../../../spaces_service';
|
||||
|
@ -36,6 +38,7 @@ describe('PUT /api/spaces/space', () => {
|
|||
const setup = async () => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpService.createRouter();
|
||||
const versionedRouterMock = router.versioned as MockedVersionedRouter;
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
|
@ -75,11 +78,12 @@ describe('PUT /api/spaces/space', () => {
|
|||
isServerless: false,
|
||||
});
|
||||
|
||||
const [routeDefinition, routeHandler] = router.put.mock.calls[0];
|
||||
const { handler, config } = versionedRouterMock.getRoute('put', '/api/spaces/space/{id}')
|
||||
.versions[API_VERSIONS.public.v1];
|
||||
|
||||
return {
|
||||
routeValidation: routeDefinition.validate as RouteValidatorConfig<{}, {}, {}>,
|
||||
routeHandler,
|
||||
routeValidation: (config.validate as any).request as RouteValidatorConfig<{}, {}, {}>,
|
||||
routeHandler: handler,
|
||||
savedObjectsRepositoryMock,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ import { schema } from '@kbn/config-schema';
|
|||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
|
||||
import type { ExternalRouteDeps } from '.';
|
||||
import type { Space } from '../../../../common';
|
||||
import { API_VERSIONS, type Space } from '../../../../common';
|
||||
import { wrapError } from '../../../lib/errors';
|
||||
import { getSpaceSchema } from '../../../lib/space_schema';
|
||||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
@ -17,37 +17,44 @@ import { createLicensedRouteHandler } from '../../lib';
|
|||
export function initPutSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { router, getSpacesService, isServerless } = deps;
|
||||
|
||||
router.put(
|
||||
{
|
||||
router.versioned
|
||||
.put({
|
||||
path: '/api/spaces/space/{id}',
|
||||
access: 'public',
|
||||
description: `Update a space`,
|
||||
options: {
|
||||
access: isServerless ? 'internal' : 'public',
|
||||
description: `Update a space`,
|
||||
tags: ['oas-tag:spaces'],
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
body: getSpaceSchema(isServerless),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
const space = request.body;
|
||||
const id = request.params.id;
|
||||
|
||||
let result: Space;
|
||||
try {
|
||||
result = await spacesClient.update(id, { ...space });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound();
|
||||
}
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
|
||||
return response.ok({ body: result });
|
||||
})
|
||||
);
|
||||
.addVersion(
|
||||
{
|
||||
version: API_VERSIONS.public.v1,
|
||||
validate: {
|
||||
request: {
|
||||
params: schema.object({
|
||||
id: schema.string(),
|
||||
}),
|
||||
body: getSpaceSchema(isServerless),
|
||||
},
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
const space = request.body;
|
||||
const id = request.params.id;
|
||||
|
||||
let result: Space;
|
||||
try {
|
||||
result = await spacesClient.update(id, { ...space });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound();
|
||||
}
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
|
||||
return response.ok({ body: result });
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
"@kbn/security-plugin-types-public",
|
||||
"@kbn/cloud-plugin",
|
||||
"@kbn/core-analytics-browser",
|
||||
"@kbn/core-analytics-browser",
|
||||
"@kbn/security-plugin-types-common",
|
||||
"@kbn/core-application-browser",
|
||||
"@kbn/unsaved-changes-prompt",
|
||||
|
@ -52,6 +51,7 @@
|
|||
"@kbn/core-notifications-browser",
|
||||
"@kbn/logging",
|
||||
"@kbn/core-logging-browser-mocks",
|
||||
"@kbn/core-http-router-server-mocks"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -44,7 +44,9 @@ export function SpacesServiceProvider({ getService }: FtrProviderContext) {
|
|||
: undefined;
|
||||
|
||||
const axios = Axios.create({
|
||||
headers: { 'kbn-xsrf': 'x-pack/ftr/services/spaces/space' },
|
||||
headers: {
|
||||
'kbn-xsrf': 'x-pack/ftr/services/spaces/space',
|
||||
},
|
||||
baseURL: url,
|
||||
maxRedirects: 0,
|
||||
validateStatus: () => true, // we do our own validation below and throw better error messages
|
||||
|
|
|
@ -26,6 +26,8 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const roleScopedSupertest = getService('roleScopedSupertest');
|
||||
const samlAuth = getService('samlAuth');
|
||||
|
||||
// CRUD operations to become public APIs: https://github.com/elastic/kibana/issues/192153
|
||||
let supertestAdminWithApiKey: SupertestWithRoleScopeType;
|
||||
let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType;
|
||||
|
@ -48,7 +50,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
describe('spaces', function () {
|
||||
before(async () => {
|
||||
supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withInternalHeaders: true,
|
||||
withCommonHeaders: true,
|
||||
});
|
||||
supertestAdminWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope(
|
||||
'admin',
|
||||
|
@ -242,42 +244,166 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
// These tests just test access to API endpoints
|
||||
// These tests just test access to API endpoints, in this case
|
||||
// when accessed without internal headers they will return 400
|
||||
// They will be included in deployment agnostic testing once spaces
|
||||
// are enabled in production.
|
||||
describe(`Access`, () => {
|
||||
it('#copyToSpace', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_copy_saved_objects'
|
||||
);
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
it('#resolveCopyToSpaceErrors', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_resolve_copy_saved_objects_errors'
|
||||
);
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
it('#updateObjectsSpaces', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_update_objects_spaces'
|
||||
);
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
it('#getShareableReferences', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_get_shareable_references')
|
||||
.send({
|
||||
objects: [{ type: 'a', id: 'a' }],
|
||||
describe(`internal`, () => {
|
||||
it('#getActiveSpace requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.get('/internal/spaces/_active_space')
|
||||
.set(samlAuth.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.get('/internal/spaces/_active_space')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'default',
|
||||
})
|
||||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('#copyToSpace requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_copy_saved_objects'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_copy_saved_objects')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
// expect 400 for missing body
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '[request body]: expected a plain object value, but found [null] instead.',
|
||||
});
|
||||
});
|
||||
|
||||
it('#resolveCopyToSpaceErrors requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_resolve_copy_saved_objects_errors'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_resolve_copy_saved_objects_errors')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
// expect 400 for missing body
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '[request body]: expected a plain object value, but found [null] instead.',
|
||||
});
|
||||
});
|
||||
|
||||
it('#updateObjectsSpaces requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_update_objects_spaces'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_update_objects_spaces')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
// expect 400 for missing body
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '[request body]: expected a plain object value, but found [null] instead.',
|
||||
});
|
||||
});
|
||||
|
||||
it('#getShareableReferences requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_get_shareable_references'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_get_shareable_references')
|
||||
.set(samlAuth.getInternalRequestHeader())
|
||||
.send({
|
||||
objects: [{ type: 'a', id: 'a' }],
|
||||
}));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
});
|
||||
});
|
||||
it('#disableLegacyUrlAliases', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_disable_legacy_url_aliases'
|
||||
);
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
|
||||
describe(`disabled`, () => {
|
||||
it('#disableLegacyUrlAliases', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_disable_legacy_url_aliases'
|
||||
);
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
describe('spaces', function () {
|
||||
before(async () => {
|
||||
// admin is the only predefined role that will work for all 3 solutions
|
||||
supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin');
|
||||
supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withCommonHeaders: true,
|
||||
});
|
||||
supertestAdminWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope(
|
||||
'admin',
|
||||
{
|
||||
|
@ -33,136 +35,36 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
describe('route access', () => {
|
||||
it('#delete', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.delete('/api/spaces/space/default')
|
||||
.set(samlAuth.getInternalRequestHeader());
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
|
||||
// Skipped due to change in QA environment for role management and spaces
|
||||
// TODO: revisit once the change is rolled out to all environments
|
||||
it.skip('#create', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/space')
|
||||
.set(samlAuth.getInternalRequestHeader())
|
||||
.send({
|
||||
describe('public (CRUD)', () => {
|
||||
// Skipped due to change in QA environment for role management and spaces
|
||||
// TODO: revisit once the change is rolled out to all environments
|
||||
it.skip('#create', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.post('/api/spaces/space').send({
|
||||
id: 'custom',
|
||||
name: 'Custom',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
it('#update requires internal header', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.put('/api/spaces/space/default')
|
||||
.set(samlAuth.getInternalRequestHeader())
|
||||
.send({
|
||||
id: 'default',
|
||||
name: 'UPDATED!',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
});
|
||||
|
||||
it('#copyToSpace', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_copy_saved_objects')
|
||||
.set(samlAuth.getInternalRequestHeader());
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
|
||||
it('#resolveCopyToSpaceErrors', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_resolve_copy_saved_objects_errors')
|
||||
.set(samlAuth.getInternalRequestHeader());
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
|
||||
it('#updateObjectsSpaces', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_update_objects_spaces')
|
||||
.set(samlAuth.getInternalRequestHeader());
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
|
||||
it('#getShareableReferences', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_get_shareable_references')
|
||||
.set(samlAuth.getInternalRequestHeader())
|
||||
.send({
|
||||
objects: [{ type: 'a', id: 'a' }],
|
||||
});
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
});
|
||||
|
||||
it('#disableLegacyUrlAliases', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_disable_legacy_url_aliases')
|
||||
.set(samlAuth.getInternalRequestHeader());
|
||||
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
describe('internal', () => {
|
||||
it('#get requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.get('/api/spaces/space/default')
|
||||
.set(samlAuth.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
// Should fail due to maximum spaces limit, not because of lacking internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
message:
|
||||
'Unable to create Space, this exceeds the maximum number of spaces set by the xpack.spaces.maxSpaces setting',
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.get('/api/spaces/space/default')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
it('#get', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get('/api/spaces/space/default');
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'default',
|
||||
})
|
||||
);
|
||||
expect(body).toEqual(expect.objectContaining({ id: 'default' }));
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('#getAll requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.get('/api/spaces/space')
|
||||
.set(samlAuth.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.get('/api/spaces/space')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
it('#getAll', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get('/api/spaces/space');
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
|
@ -174,6 +76,35 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('#update', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.put('/api/spaces/space/default')
|
||||
.send({
|
||||
id: 'default',
|
||||
name: 'UPDATED!',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
});
|
||||
|
||||
it('#delete', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.delete(
|
||||
'/api/spaces/space/default'
|
||||
);
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
// 400 with specific reason - cannot delete the default space
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: 'The default space cannot be deleted because it is reserved.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('internal', () => {
|
||||
it('#getActiveSpace requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
@ -202,6 +133,133 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('#copyToSpace requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_copy_saved_objects'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_copy_saved_objects')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
// expect 400 for missing body
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '[request body]: expected a plain object value, but found [null] instead.',
|
||||
});
|
||||
});
|
||||
|
||||
it('#resolveCopyToSpaceErrors requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_resolve_copy_saved_objects_errors'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_resolve_copy_saved_objects_errors')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
// expect 400 for missing body
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '[request body]: expected a plain object value, but found [null] instead.',
|
||||
});
|
||||
});
|
||||
|
||||
it('#updateObjectsSpaces requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_update_objects_spaces'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_update_objects_spaces')
|
||||
.set(samlAuth.getInternalRequestHeader()));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
|
||||
// expect 400 for missing body
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: '[request body]: expected a plain object value, but found [null] instead.',
|
||||
});
|
||||
});
|
||||
|
||||
it('#getShareableReferences requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey.post(
|
||||
'/api/spaces/_get_shareable_references'
|
||||
));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
|
||||
({ body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_get_shareable_references')
|
||||
.set(samlAuth.getInternalRequestHeader())
|
||||
.send({
|
||||
objects: [{ type: 'a', id: 'a' }],
|
||||
}));
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
});
|
||||
});
|
||||
|
||||
describe('disabled', () => {
|
||||
it('#disableLegacyUrlAliases', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey
|
||||
.post('/api/spaces/_disable_legacy_url_aliases')
|
||||
.set(samlAuth.getInternalRequestHeader());
|
||||
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -37,65 +37,66 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'admin',
|
||||
{
|
||||
useCookieHeader: true,
|
||||
withInternalHeaders: true,
|
||||
}
|
||||
);
|
||||
supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withInternalHeaders: true,
|
||||
withCommonHeaders: true,
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
await supertestAdminWithApiKey.destroy();
|
||||
});
|
||||
describe('route access', () => {
|
||||
describe('internal', () => {
|
||||
describe('disabled', () => {
|
||||
// Skipped due to change in QA environment for role management and spaces
|
||||
// TODO: revisit once the change is rolled out to all environments
|
||||
it.skip('get all privileges', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get('/api/security/privileges');
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
describe('disabled', () => {
|
||||
// Skipped due to change in QA environment for role management and spaces
|
||||
// TODO: revisit once the change is rolled out to all environments
|
||||
it.skip('get all privileges', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get('/api/security/privileges');
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
// Skipped due to change in QA environment for role management and spaces
|
||||
// TODO: revisit once the change is rolled out to all environments
|
||||
it.skip('get built-in elasticsearch privileges', async () => {
|
||||
const { body, status } = await supertestAdminWithCookieCredentials.get(
|
||||
'/internal/security/esPrivileges/builtin'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
// Skipped due to change in QA environment for role management and spaces
|
||||
// TODO: revisit once the change is rolled out to all environments
|
||||
it.skip('get built-in elasticsearch privileges', async () => {
|
||||
const { body, status } = await supertestAdminWithCookieCredentials.get(
|
||||
'/internal/security/esPrivileges/builtin'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('create/update roleAuthc', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.put('/api/security/role/test');
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
// Role CRUD APIs are gated behind the xpack.security.roleManagementEnabled config
|
||||
// setting. This setting is false by default on serverless. When the custom roles
|
||||
// feature is enabled, this setting will be true, and the tests from
|
||||
// roles_routes_feature_flag.ts can be moved here to replace these.
|
||||
it('create/update roleAuthc', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.put('/api/security/role/test');
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get roleAuthc', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get(
|
||||
'/api/security/role/superuser'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
it('get role', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get(
|
||||
'/api/security/role/superuser'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get all roles', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get('/api/security/role');
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
it('get all roles', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.get('/api/security/role');
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('delete roleAuthc', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.delete(
|
||||
'/api/security/role/superuser'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
it('delete role', async () => {
|
||||
const { body, status } = await supertestAdminWithApiKey.delete(
|
||||
'/api/security/role/superuser'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get shared saved object permissions', async () => {
|
||||
const { body, status } = await supertestAdminWithCookieCredentials.get(
|
||||
'/internal/security/_share_saved_object_permissions'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
it('get shared saved object permissions', async () => {
|
||||
const { body, status } = await supertestAdminWithCookieCredentials.get(
|
||||
'/internal/security/_share_saved_object_permissions'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import expect from 'expect';
|
||||
import type { Role } from '@kbn/security-plugin-types-common';
|
||||
import { SupertestWithRoleScopeType } from '@kbn/test-suites-xpack/api_integration/deployment_agnostic/services';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
@ -26,6 +26,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function ({ getService }: FtrProviderContext) {
|
||||
const platformSecurityUtils = getService('platformSecurityUtils');
|
||||
const roleScopedSupertest = getService('roleScopedSupertest');
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
let supertestAdminWithApiKey: SupertestWithRoleScopeType;
|
||||
let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType;
|
||||
const es = getService('es');
|
||||
|
@ -41,7 +42,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
}
|
||||
);
|
||||
supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withInternalHeaders: true,
|
||||
withCommonHeaders: true,
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
|
@ -86,7 +87,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(204);
|
||||
|
||||
const role = await es.security.getRole({ name: 'role_with_privileges' });
|
||||
expect(role).to.eql({
|
||||
expect(role).toEqual({
|
||||
role_with_privileges: {
|
||||
cluster: ['manage'],
|
||||
indices: [
|
||||
|
@ -425,7 +426,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(200)
|
||||
.expect((res: { body: Role[] }) => {
|
||||
const roles = res.body;
|
||||
expect(roles).to.be.an('array');
|
||||
|
||||
const success = roles.every((role) => {
|
||||
return (
|
||||
|
@ -440,8 +440,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const expectedRole = roles.find((role) => role.name === 'space_role_to_get');
|
||||
|
||||
expect(success).to.be(true);
|
||||
expect(expectedRole).to.be.an('object');
|
||||
expect(success).toBe(true);
|
||||
expect(expectedRole).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -508,7 +508,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(204);
|
||||
|
||||
const role = await es.security.getRole({ name: 'role_to_update' });
|
||||
expect(role).to.eql({
|
||||
expect(role).toEqual({
|
||||
role_to_update: {
|
||||
cluster: ['manage'],
|
||||
indices: [
|
||||
|
@ -582,9 +582,9 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
const role = await es.security.getRole({ name: 'role_to_update_with_dls_fls' });
|
||||
|
||||
expect(role.role_to_update_with_dls_fls.cluster).to.eql(['manage']);
|
||||
expect(role.role_to_update_with_dls_fls.indices[0].names).to.eql(['logstash-*']);
|
||||
expect(role.role_to_update_with_dls_fls.indices[0].query).to.eql(
|
||||
expect(role.role_to_update_with_dls_fls.cluster).toEqual(['manage']);
|
||||
expect(role.role_to_update_with_dls_fls.indices[0].names).toEqual(['logstash-*']);
|
||||
expect(role.role_to_update_with_dls_fls.indices[0].query).toEqual(
|
||||
`{ "match": { "geo.src": "CN" } }`
|
||||
);
|
||||
});
|
||||
|
@ -652,7 +652,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(400);
|
||||
|
||||
const role = await es.security.getRole({ name: 'role_to_update' });
|
||||
expect(role).to.eql({
|
||||
expect(role).toEqual({
|
||||
role_to_update: {
|
||||
cluster: ['monitor'],
|
||||
indices: [
|
||||
|
@ -753,7 +753,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(400);
|
||||
|
||||
const role = await es.security.getRole({ name: 'role_to_update' });
|
||||
expect(role).to.eql({
|
||||
expect(role).toEqual({
|
||||
role_to_update: {
|
||||
cluster: ['monitor'],
|
||||
indices: [
|
||||
|
@ -855,7 +855,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(400);
|
||||
|
||||
const role = await es.security.getRole({ name: 'role_to_update' });
|
||||
expect(role).to.eql({
|
||||
expect(role).toEqual({
|
||||
role_to_update: {
|
||||
cluster: ['monitor'],
|
||||
indices: [
|
||||
|
@ -924,7 +924,26 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
{ name: 'role_to_delete' },
|
||||
{ ignore: [404] }
|
||||
);
|
||||
expect(deletedRole).to.eql({});
|
||||
expect(deletedRole).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Access', () => {
|
||||
describe('public', () => {
|
||||
it('reset session page', async () => {
|
||||
const { status } = await supertestAdminWithCookieCredentials.get(
|
||||
'/internal/security/reset_session_page.js'
|
||||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
});
|
||||
describe('Disabled', () => {
|
||||
it('get shared saved object permissions', async () => {
|
||||
const { body, status } = await supertestAdminWithCookieCredentials.get(
|
||||
'/internal/security/_share_saved_object_permissions'
|
||||
);
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue