diff --git a/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts index e79c418eeb3cc..38e98104d41bd 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('Column Identifier Expressions', () => { it('can parse un-quoted identifiers', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts index 30d44d447387e..6fb176c9624f7 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('commands', () => { describe('correctly formatted, basic usage', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts index 101661973a692..f2f0fded57ca5 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('FROM', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts index 8ec533816a56e..9d822f78f9333 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { Walker } from '../../walker'; describe('function AST nodes', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts index d0650ab3f3213..889ca2a2ecf3d 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { ESQLFunction, ESQLInlineCast, ESQLSingleAstItem } from '../../types'; describe('Inline cast (::)', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts index 514d769d5c45e..7f50198c96047 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { ESQLLiteral } from '../../types'; describe('literal expression', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts index 54ddc49c5d048..d33c94e8903ac 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('METRICS', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts index 8586236eeb2f9..e4b1a892d32d9 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { Walker } from '../../walker'; /** diff --git a/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts index 4acad891150b2..214e5c1d36882 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('RENAME', () => { /** diff --git a/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts index cfaec0a6e39e9..981eac40b68ae 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('SORT', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts index d507b559fd407..f3f6aeb886ec0 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('WHERE', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/parser.ts b/packages/kbn-esql-ast/src/parser/parser.ts index ad263a49ebd00..f99e00e92d1e0 100644 --- a/packages/kbn-esql-ast/src/parser/parser.ts +++ b/packages/kbn-esql-ast/src/parser/parser.ts @@ -100,33 +100,66 @@ export interface ParseResult { } export const parse = (text: string | undefined, options: ParseOptions = {}): ParseResult => { - if (text == null) { - const commands: ESQLAstQueryExpression['commands'] = []; - return { ast: commands, root: Builder.expression.query(commands), errors: [], tokens: [] }; + try { + if (text == null) { + const commands: ESQLAstQueryExpression['commands'] = []; + return { ast: commands, root: Builder.expression.query(commands), errors: [], tokens: [] }; + } + const errorListener = new ESQLErrorListener(); + const parseListener = new ESQLAstBuilderListener(); + const { tokens, parser } = getParser( + CharStreams.fromString(text), + errorListener, + parseListener + ); + + parser[GRAMMAR_ROOT_RULE](); + + const errors = errorListener.getErrors().filter((error) => { + return !SYNTAX_ERRORS_TO_IGNORE.includes(error.message); + }); + const { ast: commands } = parseListener.getAst(); + const root = Builder.expression.query(commands, { + location: { + min: 0, + max: text.length - 1, + }, + }); + + if (options.withFormatting) { + const decorations = collectDecorations(tokens); + attachDecorations(root, tokens.tokens, decorations.lines); + } + + return { root, ast: commands, errors, tokens: tokens.tokens }; + } catch (error) { + /** + * Parsing should never fail, meaning this branch should never execute. But + * if it does fail, we want to log the error message for easier debugging. + */ + // eslint-disable-next-line no-console + console.error(error); + + const root = Builder.expression.query(); + + return { + root, + ast: root.commands, + errors: [ + { + startLineNumber: 0, + endLineNumber: 0, + startColumn: 0, + endColumn: 0, + message: + 'Parsing internal error: ' + + (!!error && typeof error === 'object' ? String(error.message) : String(error)), + severity: 'error', + }, + ], + tokens: [], + }; } - const errorListener = new ESQLErrorListener(); - const parseListener = new ESQLAstBuilderListener(); - const { tokens, parser } = getParser(CharStreams.fromString(text), errorListener, parseListener); - - parser[GRAMMAR_ROOT_RULE](); - - const errors = errorListener.getErrors().filter((error) => { - return !SYNTAX_ERRORS_TO_IGNORE.includes(error.message); - }); - const { ast: commands } = parseListener.getAst(); - const root = Builder.expression.query(commands, { - location: { - min: 0, - max: text.length - 1, - }, - }); - - if (options.withFormatting) { - const decorations = collectDecorations(tokens); - attachDecorations(root, tokens.tokens, decorations.lines); - } - - return { root, ast: commands, errors, tokens: tokens.tokens }; }; export const parseErrors = (text: string) => { diff --git a/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts b/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts index 0757304199d8a..6a7a3fc80343e 100644 --- a/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts +++ b/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts @@ -40,7 +40,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const security = getService('security'); - describe('discover data grid context tests', () => { + // Failing: See https://github.com/elastic/kibana/issues/196120 + describe.skip('discover data grid context tests', () => { before(async () => { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts index 4d453f64b6954..4f486d3337632 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts @@ -13,11 +13,11 @@ import { ConfigSchema } from '../config'; import { encryptionKeyRotationServiceMock } from '../crypto/index.mock'; export const routeDefinitionParamsMock = { - create: (config: Record = {}) => ({ + create: (config: Record = {}, buildFlavor: BuildFlavor = 'traditional') => ({ router: httpServiceMock.createRouter(), logger: loggingSystemMock.create().get(), config: ConfigSchema.validate(config) as ConfigType, encryptionKeyRotationService: encryptionKeyRotationServiceMock.create(), - buildFlavor: 'traditional' as BuildFlavor, + buildFlavor, }), }; diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts index 9b9dba6108ff3..f387e94e80990 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts @@ -44,6 +44,7 @@ describe('Key rotation routes', () => { it('correctly defines route.', () => { expect(routeConfig.options).toEqual({ + access: 'public', tags: ['access:rotateEncryptionKey', 'oas-tag:saved objects'], summary: `Rotate a key for encrypted saved objects`, description: `If a saved object cannot be decrypted using the primary encryption key, Kibana attempts to decrypt it using the specified decryption-only keys. In most of the cases this overhead is negligible, but if you're dealing with a large number of saved objects and experiencing performance issues, you may want to rotate the encryption key. @@ -83,6 +84,25 @@ describe('Key rotation routes', () => { ); }); + it('defines route as internal when build flavor is serverless', () => { + const routeParamsMock = routeDefinitionParamsMock.create( + { keyRotation: { decryptionOnlyKeys: ['b'.repeat(32)] } }, + 'serverless' + ); + defineKeyRotationRoutes(routeParamsMock); + const [config] = routeParamsMock.router.post.mock.calls.find( + ([{ path }]) => path === '/api/encrypted_saved_objects/_rotate_key' + )!; + + expect(config.options).toEqual({ + access: 'internal', + tags: ['access:rotateEncryptionKey', 'oas-tag:saved objects'], + summary: `Rotate a key for encrypted saved objects`, + description: `If a saved object cannot be decrypted using the primary encryption key, Kibana attempts to decrypt it using the specified decryption-only keys. In most of the cases this overhead is negligible, but if you're dealing with a large number of saved objects and experiencing performance issues, you may want to rotate the encryption key. + NOTE: Bulk key rotation can consume a considerable amount of resources and hence only user with a superuser role can trigger it.`, + }); + }); + it('returns 400 if decryption only keys are not specified.', async () => { const routeParamsMock = routeDefinitionParamsMock.create(); defineKeyRotationRoutes(routeParamsMock); diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts index 80907497010da..272e74c3a69cb 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts @@ -41,7 +41,7 @@ export function defineKeyRotationRoutes({ }, options: { tags: ['access:rotateEncryptionKey', 'oas-tag:saved objects'], - access: buildFlavor === 'serverless' ? 'internal' : undefined, + access: buildFlavor === 'serverless' ? 'internal' : 'public', summary: `Rotate a key for encrypted saved objects`, description: `If a saved object cannot be decrypted using the primary encryption key, Kibana attempts to decrypt it using the specified decryption-only keys. In most of the cases this overhead is negligible, but if you're dealing with a large number of saved objects and experiencing performance issues, you may want to rotate the encryption key. NOTE: Bulk key rotation can consume a considerable amount of resources and hence only user with a superuser role can trigger it.`, diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index b1473c56b37ed..efc564089fb43 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -173,7 +173,7 @@ export const SearchBar: FC = (opts) => { reportEvent.searchRequest(); } - const rawParams = parseSearchParams(searchValue.toLowerCase()); + const rawParams = parseSearchParams(searchValue.toLowerCase(), searchableTypes); let tagIds: string[] | undefined; if (taggingApi && rawParams.filters.tags) { tagIds = rawParams.filters.tags.map( diff --git a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts index c6df745be847f..8e24f599ce1d2 100644 --- a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts +++ b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts @@ -9,12 +9,12 @@ import { parseSearchParams } from './parse_search_params'; describe('parseSearchParams', () => { it('returns the correct term', () => { - const searchParams = parseSearchParams('tag:(my-tag OR other-tag) hello'); + const searchParams = parseSearchParams('tag:(my-tag OR other-tag) hello', []); expect(searchParams.term).toEqual('hello'); }); it('returns the raw query as `term` in case of parsing error', () => { - const searchParams = parseSearchParams('tag:((()^invalid'); + const searchParams = parseSearchParams('tag:((()^invalid', []); expect(searchParams).toEqual({ term: 'tag:((()^invalid', filters: {}, @@ -22,12 +22,12 @@ describe('parseSearchParams', () => { }); it('returns `undefined` term if query only contains field clauses', () => { - const searchParams = parseSearchParams('tag:(my-tag OR other-tag)'); + const searchParams = parseSearchParams('tag:(my-tag OR other-tag)', []); expect(searchParams.term).toBeUndefined(); }); it('returns correct filters when no field clause is defined', () => { - const searchParams = parseSearchParams('hello'); + const searchParams = parseSearchParams('hello', []); expect(searchParams.filters).toEqual({ tags: undefined, types: undefined, @@ -35,7 +35,7 @@ describe('parseSearchParams', () => { }); it('returns correct filters when field clauses are present', () => { - const searchParams = parseSearchParams('tag:foo type:bar hello tag:dolly'); + const searchParams = parseSearchParams('tag:foo type:bar hello tag:dolly', []); expect(searchParams).toEqual({ term: 'hello', filters: { @@ -46,7 +46,7 @@ describe('parseSearchParams', () => { }); it('considers unknown field clauses to be part of the raw search term', () => { - const searchParams = parseSearchParams('tag:foo unknown:bar hello'); + const searchParams = parseSearchParams('tag:foo unknown:bar hello', []); expect(searchParams).toEqual({ term: 'unknown:bar hello', filters: { @@ -56,7 +56,7 @@ describe('parseSearchParams', () => { }); it('handles aliases field clauses', () => { - const searchParams = parseSearchParams('tag:foo tags:bar type:dash types:board hello'); + const searchParams = parseSearchParams('tag:foo tags:bar type:dash types:board hello', []); expect(searchParams).toEqual({ term: 'hello', filters: { @@ -67,7 +67,7 @@ describe('parseSearchParams', () => { }); it('converts boolean and number values to string for known filters', () => { - const searchParams = parseSearchParams('tag:42 tags:true type:69 types:false hello'); + const searchParams = parseSearchParams('tag:42 tags:true type:69 types:false hello', []); expect(searchParams).toEqual({ term: 'hello', filters: { @@ -76,4 +76,74 @@ describe('parseSearchParams', () => { }, }); }); + + it('converts multiword searchable types to phrases so they get picked up as types', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad', 'enterprise search']; + const searchParams = parseSearchParams( + 'type:canvas workpad types:canvas-workpad hello type:enterprise search type:not multiword', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello multiword', + filters: { + types: ['canvas workpad', 'enterprise search', 'not'], + }, + }); + }); + + it('parses correctly when multiword types are already quoted', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad']; + const searchParams = parseSearchParams( + `type:"canvas workpad" hello type:"dashboard"`, + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['canvas workpad', 'dashboard'], + }, + }); + }); + + it('parses correctly when there is whitespace between type keyword and value', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad']; + const searchParams = parseSearchParams( + 'type: canvas workpad hello type: dashboard', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['canvas workpad', 'dashboard'], + }, + }); + }); + + it('dedupes duplicate types', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad']; + const searchParams = parseSearchParams( + 'type:canvas workpad hello type:dashboard type:canvas-workpad type:canvas workpad type:dashboard', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['canvas workpad', 'dashboard'], + }, + }); + }); + + it('handles whitespace removal even if there are no multiword types', () => { + const mockSearchableMultiwordTypes: string[] = []; + const searchParams = parseSearchParams( + 'hello type: dashboard', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['dashboard'], + }, + }); + }); }); diff --git a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts index 1df6c1123a328..90ba36cce5fcb 100644 --- a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts +++ b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts @@ -16,12 +16,54 @@ const aliasMap = { type: ['types'], }; -export const parseSearchParams = (term: string): ParsedSearchParams => { +// Converts multiword types to phrases by wrapping them in quotes and trimming whitespace after type keyword. Example: type: canvas workpad -> type:"canvas workpad". If the type is already wrapped in quotes or is a single word, it will only trim whitespace after type keyword. +const convertMultiwordTypesToPhrasesAndTrimWhitespace = ( + term: string, + multiWordTypes: string[] +): string => { + if (!multiWordTypes.length) { + return term.replace( + /(type:|types:)\s*([^"']*?)\b([^"'\s]+)/gi, + (_, typeKeyword, whitespace, typeValue) => `${typeKeyword}${whitespace.trim()}${typeValue}` + ); + } + + const typesPattern = multiWordTypes.join('|'); + const termReplaceRegex = new RegExp( + `(type:|types:)\\s*([^"']*?)\\b((${typesPattern})\\b|[^\\s"']+)`, + 'gi' + ); + + return term.replace(termReplaceRegex, (_, typeKeyword, whitespace, typeValue) => { + const trimmedTypeKeyword = `${typeKeyword}${whitespace.trim()}`; + + // If the type value is already wrapped in quotes, leave it as is + return /['"]/.test(typeValue) + ? `${trimmedTypeKeyword}${typeValue}` + : `${trimmedTypeKeyword}"${typeValue}"`; + }); +}; + +const dedupeTypes = (types: FilterValues): FilterValues => [ + ...new Set(types.map((item) => item.replace(/[-\s]+/g, ' ').trim())), +]; + +export const parseSearchParams = (term: string, searchableTypes: string[]): ParsedSearchParams => { const recognizedFields = knownFilters.concat(...Object.values(aliasMap)); let query: Query; + // Finds all multiword types that are separated by whitespace or hyphens + const multiWordSearchableTypesWhitespaceSeperated = searchableTypes + .filter((item) => /[ -]/.test(item)) + .map((item) => item.replace(/-/g, ' ')); + + const modifiedTerm = convertMultiwordTypesToPhrasesAndTrimWhitespace( + term, + multiWordSearchableTypesWhitespaceSeperated + ); + try { - query = Query.parse(term, { + query = Query.parse(modifiedTerm, { schema: { recognizedFields }, }); } catch (e) { @@ -42,7 +84,7 @@ export const parseSearchParams = (term: string): ParsedSearchParams => { term: searchTerm, filters: { tags: tags ? valuesToString(tags) : undefined, - types: types ? valuesToString(types) : undefined, + types: types ? dedupeTypes(valuesToString(types)) : undefined, }, }; }; diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx index 6f13c33585bca..7941881ff2c51 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { EntityManagerUnauthorizedError } from '@kbn/entityManager-plugin/public'; import type { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; +import { TechnicalPreviewBadge } from '@kbn/observability-shared-plugin/public'; import { useKibana } from '../../hooks/use_kibana'; import { Unauthorized } from './unauthorized_modal'; @@ -57,6 +58,8 @@ export function EnableEntityModelButton({ onSuccess }: { onSuccess: () => void } data-test-subj="inventoryInventoryPageTemplateFilledButton" fill onClick={handleEnablement} + iconType={() => } + iconSide="right" > {i18n.translate('xpack.inventory.noData.card.button', { defaultMessage: 'Enable', diff --git a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx index 3b12e11d2ba7c..79db53f39c346 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx @@ -33,8 +33,20 @@ export function getEntityManagerEnablement({ description: ( + {i18n.translate('xpack.inventory.noData.card.description.inventory', { + defaultMessage: 'Inventory', + })} + + ), link: ( { access: 'public', authRequired: false, tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], + excludeFromOAS: true, }); expect(routeConfig.validate).toEqual({ body: undefined, @@ -170,7 +171,7 @@ describe('Common authentication routes', () => { }); it('correctly defines route.', async () => { - expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.options).toEqual({ access: 'internal' }); expect(routeConfig.validate).toBe(false); }); diff --git a/x-pack/plugins/security/server/routes/authentication/common.ts b/x-pack/plugins/security/server/routes/authentication/common.ts index e54d6e35f1669..b519171fd4fe6 100644 --- a/x-pack/plugins/security/server/routes/authentication/common.ts +++ b/x-pack/plugins/security/server/routes/authentication/common.ts @@ -48,6 +48,7 @@ export function defineCommonRoutes({ validate: { query: schema.object({}, { unknowns: 'allow' }) }, options: { access: 'public', + excludeFromOAS: true, authRequired: false, tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], }, @@ -89,10 +90,11 @@ export function defineCommonRoutes({ '/internal/security/me', ...(buildFlavor !== 'serverless' ? ['/api/security/v1/me'] : []), ]) { + const deprecated = path === '/api/security/v1/me'; router.get( - { path, validate: false }, + { path, validate: false, options: { access: deprecated ? 'public' : 'internal' } }, createLicensedRouteHandler(async (context, request, response) => { - if (path === '/api/security/v1/me') { + if (deprecated) { logger.warn( `The "${basePath.serverBasePath}${path}" endpoint is deprecated and will be removed in the next major version.`, { tags: ['deprecation'] } diff --git a/x-pack/plugins/security/server/routes/authentication/oidc.ts b/x-pack/plugins/security/server/routes/authentication/oidc.ts index 2c4ab9de1491b..69c3ce1700671 100644 --- a/x-pack/plugins/security/server/routes/authentication/oidc.ts +++ b/x-pack/plugins/security/server/routes/authentication/oidc.ts @@ -37,7 +37,7 @@ export function defineOIDCRoutes({ { path, validate: false, - options: { authRequired: false }, + options: { authRequired: false, excludeFromOAS: true }, }, (context, request, response) => { const serverBasePath = basePath.serverBasePath; @@ -68,7 +68,7 @@ export function defineOIDCRoutes({ { path: '/internal/security/oidc/implicit.js', validate: false, - options: { authRequired: false }, + options: { authRequired: false, excludeFromOAS: true }, }, (context, request, response) => { const serverBasePath = basePath.serverBasePath; @@ -106,7 +106,12 @@ export function defineOIDCRoutes({ { unknowns: 'allow' } ), }, - options: { authRequired: false, tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW] }, + options: { + access: 'public', + excludeFromOAS: true, + authRequired: false, + tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], + }, }, createLicensedRouteHandler(async (context, request, response) => { const serverBasePath = basePath.serverBasePath; @@ -184,6 +189,8 @@ export function defineOIDCRoutes({ ), }, options: { + access: 'public', + excludeFromOAS: true, authRequired: false, xsrfRequired: false, tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], @@ -227,6 +234,8 @@ export function defineOIDCRoutes({ ), }, options: { + access: 'public', + excludeFromOAS: true, authRequired: false, tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], }, diff --git a/x-pack/plugins/security/server/routes/authentication/saml.test.ts b/x-pack/plugins/security/server/routes/authentication/saml.test.ts index e952d98a38649..f693d20354e89 100644 --- a/x-pack/plugins/security/server/routes/authentication/saml.test.ts +++ b/x-pack/plugins/security/server/routes/authentication/saml.test.ts @@ -56,6 +56,7 @@ describe('SAML authentication routes', () => { expect(routeConfig.options).toEqual({ access: 'public', authRequired: false, + excludeFromOAS: true, xsrfRequired: false, tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], }); diff --git a/x-pack/plugins/security/server/routes/authentication/saml.ts b/x-pack/plugins/security/server/routes/authentication/saml.ts index ddc31fbc88b89..3c72fd908e6c4 100644 --- a/x-pack/plugins/security/server/routes/authentication/saml.ts +++ b/x-pack/plugins/security/server/routes/authentication/saml.ts @@ -38,6 +38,7 @@ export function defineSAMLRoutes({ }, options: { access: 'public', + excludeFromOAS: true, authRequired: false, xsrfRequired: false, tags: [ROUTE_TAG_CAN_REDIRECT, ROUTE_TAG_AUTH_FLOW], diff --git a/x-pack/plugins/security/server/routes/authorization/privileges/get.ts b/x-pack/plugins/security/server/routes/authorization/privileges/get.ts index 1d278aa676ac3..b7204faaa7ca4 100644 --- a/x-pack/plugins/security/server/routes/authorization/privileges/get.ts +++ b/x-pack/plugins/security/server/routes/authorization/privileges/get.ts @@ -26,6 +26,7 @@ export function defineGetPrivilegesRoutes({ router, authz }: RouteDefinitionPara ), }), }, + options: { access: 'public' }, }, createLicensedRouteHandler((context, request, response) => { const respectLicenseLevel = request.query.respectLicenseLevel !== 'false'; // if undefined resolve to true by default diff --git a/x-pack/plugins/security/server/routes/authorization/reset_session_page.ts b/x-pack/plugins/security/server/routes/authorization/reset_session_page.ts index 67254735b9a16..0af24ad8d8397 100644 --- a/x-pack/plugins/security/server/routes/authorization/reset_session_page.ts +++ b/x-pack/plugins/security/server/routes/authorization/reset_session_page.ts @@ -12,7 +12,7 @@ export function resetSessionPageRoutes({ httpResources }: RouteDefinitionParams) { path: '/internal/security/reset_session_page.js', validate: false, - options: { authRequired: false }, + options: { authRequired: false, excludeFromOAS: true }, }, (context, request, response) => { return response.renderJs({ diff --git a/x-pack/plugins/security/server/routes/session_management/index.ts b/x-pack/plugins/security/server/routes/session_management/index.ts index c095a77409975..041feea8a62fd 100644 --- a/x-pack/plugins/security/server/routes/session_management/index.ts +++ b/x-pack/plugins/security/server/routes/session_management/index.ts @@ -13,12 +13,5 @@ import type { RouteDefinitionParams } from '..'; export function defineSessionManagementRoutes(params: RouteDefinitionParams) { defineSessionInfoRoutes(params); defineSessionExtendRoutes(params); - - // The invalidate session API was introduced to address situations where the session index - // could grow rapidly - when session timeouts are disabled, or with anonymous access. - // In the serverless environment, sessions timeouts are always be enabled, and there is no - // anonymous access. This eliminates the need for an invalidate session HTTP API. - if (params.buildFlavor !== 'serverless') { - defineInvalidateSessionsRoutes(params); - } + defineInvalidateSessionsRoutes(params); } diff --git a/x-pack/plugins/security/server/routes/session_management/invalidate.ts b/x-pack/plugins/security/server/routes/session_management/invalidate.ts index c7d27b835edf2..a45d8f00c1ca4 100644 --- a/x-pack/plugins/security/server/routes/session_management/invalidate.ts +++ b/x-pack/plugins/security/server/routes/session_management/invalidate.ts @@ -12,7 +12,11 @@ import type { RouteDefinitionParams } from '..'; /** * Defines routes required for session invalidation. */ -export function defineInvalidateSessionsRoutes({ router, getSession }: RouteDefinitionParams) { +export function defineInvalidateSessionsRoutes({ + router, + getSession, + buildFlavor, +}: RouteDefinitionParams) { router.post( { path: '/api/security/session/_invalidate', @@ -34,7 +38,12 @@ export function defineInvalidateSessionsRoutes({ router, getSession }: RouteDefi }), }, options: { - access: 'public', + // The invalidate session API was introduced to address situations where the session index + // could grow rapidly - when session timeouts are disabled, or with anonymous access. + // In the serverless environment, sessions timeouts are always be enabled, and there is no + // anonymous access. However, keeping this endpoint available internally in serverless would + // be useful in situations where we need to batch-invalidate user sessions. + access: buildFlavor === 'serverless' ? 'internal' : 'public', tags: ['access:sessionManagement'], summary: `Invalidate user sessions`, }, diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts index ef588ae1cfcfc..74eee1e129e2c 100644 --- a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts +++ b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts @@ -71,7 +71,7 @@ describe('Access agreement view routes', () => { }); it('correctly defines route.', () => { - expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.options).toEqual({ excludeFromOAS: true }); expect(routeConfig.validate).toBe(false); }); diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.ts b/x-pack/plugins/security/server/routes/views/access_agreement.ts index 3724892edd6df..823fbb0286f33 100644 --- a/x-pack/plugins/security/server/routes/views/access_agreement.ts +++ b/x-pack/plugins/security/server/routes/views/access_agreement.ts @@ -24,7 +24,7 @@ export function defineAccessAgreementRoutes({ const canHandleRequest = () => license.getFeatures().allowAccessAgreement; httpResources.register( - { path: '/security/access_agreement', validate: false }, + { path: '/security/access_agreement', validate: false, options: { excludeFromOAS: true } }, createLicensedRouteHandler(async (context, request, response) => canHandleRequest() ? response.renderCoreApp() diff --git a/x-pack/plugins/security/server/routes/views/account_management.ts b/x-pack/plugins/security/server/routes/views/account_management.ts index af49f325a25d2..4b3fbb78fed90 100644 --- a/x-pack/plugins/security/server/routes/views/account_management.ts +++ b/x-pack/plugins/security/server/routes/views/account_management.ts @@ -11,7 +11,8 @@ import type { RouteDefinitionParams } from '..'; * Defines routes required for the Account Management view. */ export function defineAccountManagementRoutes({ httpResources }: RouteDefinitionParams) { - httpResources.register({ path: '/security/account', validate: false }, (context, req, res) => - res.renderCoreApp() + httpResources.register( + { path: '/security/account', validate: false, options: { excludeFromOAS: true } }, + (context, req, res) => res.renderCoreApp() ); } diff --git a/x-pack/plugins/security/server/routes/views/capture_url.test.ts b/x-pack/plugins/security/server/routes/views/capture_url.test.ts index 1893ad6c9cb5f..4496ab341b085 100644 --- a/x-pack/plugins/security/server/routes/views/capture_url.test.ts +++ b/x-pack/plugins/security/server/routes/views/capture_url.test.ts @@ -34,7 +34,7 @@ describe('Capture URL view routes', () => { }); it('correctly defines route.', () => { - expect(routeConfig.options).toEqual({ authRequired: false }); + expect(routeConfig.options).toEqual({ authRequired: false, excludeFromOAS: true }); expect(routeConfig.validate).toEqual({ body: undefined, diff --git a/x-pack/plugins/security/server/routes/views/capture_url.ts b/x-pack/plugins/security/server/routes/views/capture_url.ts index 8eff92d78999d..394b799ca1f9d 100644 --- a/x-pack/plugins/security/server/routes/views/capture_url.ts +++ b/x-pack/plugins/security/server/routes/views/capture_url.ts @@ -19,7 +19,7 @@ export function defineCaptureURLRoutes({ httpResources }: RouteDefinitionParams) validate: { query: schema.object({ next: schema.maybe(schema.string()) }, { unknowns: 'ignore' }), }, - options: { authRequired: false }, + options: { authRequired: false, excludeFromOAS: true }, }, (context, request, response) => response.renderAnonymousCoreApp() ); diff --git a/x-pack/plugins/security/server/routes/views/logged_out.test.ts b/x-pack/plugins/security/server/routes/views/logged_out.test.ts index 850a533e3d93a..9aecb39750b1b 100644 --- a/x-pack/plugins/security/server/routes/views/logged_out.test.ts +++ b/x-pack/plugins/security/server/routes/views/logged_out.test.ts @@ -35,7 +35,7 @@ describe('LoggedOut view routes', () => { }); it('correctly defines route.', () => { - expect(routeConfig.options).toEqual({ authRequired: false }); + expect(routeConfig.options).toEqual({ authRequired: false, excludeFromOAS: true }); expect(routeConfig.validate).toBe(false); }); diff --git a/x-pack/plugins/security/server/routes/views/logged_out.ts b/x-pack/plugins/security/server/routes/views/logged_out.ts index 360c0fb2c9b7c..66581f574def8 100644 --- a/x-pack/plugins/security/server/routes/views/logged_out.ts +++ b/x-pack/plugins/security/server/routes/views/logged_out.ts @@ -20,7 +20,7 @@ export function defineLoggedOutRoutes({ { path: '/security/logged_out', validate: false, - options: { authRequired: false }, + options: { authRequired: false, excludeFromOAS: true }, }, async (context, request, response) => { // Authentication flow isn't triggered automatically for this route, so we should explicitly diff --git a/x-pack/plugins/security/server/routes/views/login.test.ts b/x-pack/plugins/security/server/routes/views/login.test.ts index b19ef41ca9098..11797e20523e9 100644 --- a/x-pack/plugins/security/server/routes/views/login.test.ts +++ b/x-pack/plugins/security/server/routes/views/login.test.ts @@ -52,7 +52,7 @@ describe('Login view routes', () => { }); it('correctly defines route.', () => { - expect(routeConfig.options).toEqual({ authRequired: 'optional' }); + expect(routeConfig.options).toEqual({ authRequired: 'optional', excludeFromOAS: true }); expect(routeConfig.validate).toEqual({ body: undefined, diff --git a/x-pack/plugins/security/server/routes/views/login.ts b/x-pack/plugins/security/server/routes/views/login.ts index 5d4468fcbba57..8cf8459d523b8 100644 --- a/x-pack/plugins/security/server/routes/views/login.ts +++ b/x-pack/plugins/security/server/routes/views/login.ts @@ -39,7 +39,7 @@ export function defineLoginRoutes({ { unknowns: 'allow' } ), }, - options: { authRequired: 'optional' }, + options: { authRequired: 'optional', excludeFromOAS: true }, }, async (context, request, response) => { // Default to true if license isn't available or it can't be resolved for some reason. diff --git a/x-pack/plugins/security/server/routes/views/logout.ts b/x-pack/plugins/security/server/routes/views/logout.ts index 3fb905ee10d37..d61f4e83083d2 100644 --- a/x-pack/plugins/security/server/routes/views/logout.ts +++ b/x-pack/plugins/security/server/routes/views/logout.ts @@ -12,7 +12,7 @@ import type { RouteDefinitionParams } from '..'; */ export function defineLogoutRoutes({ httpResources }: RouteDefinitionParams) { httpResources.register( - { path: '/logout', validate: false, options: { authRequired: false } }, + { path: '/logout', validate: false, options: { authRequired: false, excludeFromOAS: true } }, (context, request, response) => response.renderAnonymousCoreApp() ); } diff --git a/x-pack/plugins/security/server/routes/views/overwritten_session.ts b/x-pack/plugins/security/server/routes/views/overwritten_session.ts index 115f7ea0a093f..4ab57f2cc9e72 100644 --- a/x-pack/plugins/security/server/routes/views/overwritten_session.ts +++ b/x-pack/plugins/security/server/routes/views/overwritten_session.ts @@ -12,7 +12,7 @@ import type { RouteDefinitionParams } from '..'; */ export function defineOverwrittenSessionRoutes({ httpResources }: RouteDefinitionParams) { httpResources.register( - { path: '/security/overwritten_session', validate: false }, + { path: '/security/overwritten_session', validate: false, options: { excludeFromOAS: true } }, (context, req, res) => res.renderCoreApp() ); } diff --git a/x-pack/plugins/spaces/server/routes/views/index.test.ts b/x-pack/plugins/spaces/server/routes/views/index.test.ts index b87bfe86c022a..e42f2dcf42eaf 100644 --- a/x-pack/plugins/spaces/server/routes/views/index.test.ts +++ b/x-pack/plugins/spaces/server/routes/views/index.test.ts @@ -59,7 +59,7 @@ describe('Space Selector view routes', () => { }); it('correctly defines route.', () => { - expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.options).toEqual({ excludeFromOAS: true }); expect(routeConfig.validate).toBe(false); }); diff --git a/x-pack/plugins/spaces/server/routes/views/index.ts b/x-pack/plugins/spaces/server/routes/views/index.ts index ab06b17374f13..f21a665e35525 100644 --- a/x-pack/plugins/spaces/server/routes/views/index.ts +++ b/x-pack/plugins/spaces/server/routes/views/index.ts @@ -20,7 +20,7 @@ export interface ViewRouteDeps { export function initSpacesViewsRoutes(deps: ViewRouteDeps) { deps.httpResources.register( - { path: '/spaces/space_selector', validate: false }, + { path: '/spaces/space_selector', validate: false, options: { excludeFromOAS: true } }, (context, request, response) => response.renderCoreApp() ); @@ -32,6 +32,7 @@ export function initSpacesViewsRoutes(deps: ViewRouteDeps) { schema.object({ next: schema.maybe(schema.string()) }, { unknowns: 'ignore' }) ), }, + options: { excludeFromOAS: true }, }, async (context, request, response) => { try { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts index 3e3eb594cc0bc..0fb8644f5e93c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts @@ -47,7 +47,9 @@ export default ({ getService }: FtrProviderContext): void => { const createWebHookConnector = () => createConnector(getWebHookAction()); // Failing: See https://github.com/elastic/kibana/issues/173804 - describe('@ess perform_bulk_action - ESS specific logic', () => { + // Failing: See https://github.com/elastic/kibana/issues/196470 + // Failing: See https://github.com/elastic/kibana/issues/196462 + describe.skip('@ess perform_bulk_action - ESS specific logic', () => { beforeEach(async () => { await deleteAllRules(supertest, log); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts index c10144aec0342..a7d32767f50ce 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts @@ -14,7 +14,8 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const utils = EntityStoreUtils(getService); - describe('@ess @skipInServerlessMKI Entity Store Engine APIs', () => { + // Failing: See https://github.com/elastic/kibana/issues/196526 + describe.skip('@ess @skipInServerlessMKI Entity Store Engine APIs', () => { const dataView = dataViewRouteHelpersFactory(supertest); before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts index de949730d3d10..481f7aa4056f6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts @@ -18,7 +18,8 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { const supertest = getService('supertest'); const utils = EntityStoreUtils(getService, namespace); - describe('@ess Entity Store Engine APIs in non-default space', () => { + // Failing: See https://github.com/elastic/kibana/issues/196546 + describe.skip('@ess Entity Store Engine APIs in non-default space', () => { const dataView = dataViewRouteHelpersFactory(supertest, namespace); before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts index 5d1fefadb2f65..027c0a20262a8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts @@ -15,7 +15,8 @@ export default function ({ getService }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const supertest = getService('supertest'); - describe('Note - Saved Objects', () => { + // Failing: See https://github.com/elastic/kibana/issues/196492 + describe.skip('Note - Saved Objects', () => { const es = getService('es'); before(() => kibanaServer.savedObjects.cleanStandardList()); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/sessions.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/sessions.ts index c76ccb81f8ce2..c102f502f9489 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/sessions.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/sessions.ts @@ -10,7 +10,6 @@ import { SupertestWithRoleScopeType } from '@kbn/test-suites-xpack/api_integrati import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); const samlAuth = getService('samlAuth'); const roleScopedSupertest = getService('roleScopedSupertest'); let supertestViewerWithCookieCredentials: SupertestWithRoleScopeType; @@ -26,16 +25,6 @@ export default function ({ getService }: FtrProviderContext) { }); describe('route access', () => { - describe('disabled', () => { - it('invalidate', async () => { - const { body, status } = await supertestViewerWithCookieCredentials - .post('/api/security/session/_invalidate') - .set(samlAuth.getInternalRequestHeader()) - .send({ match: 'all' }); - svlCommonApi.assertApiNotFound(body, status); - }); - }); - describe('internal', () => { it('get session info', async () => { let body: any; @@ -84,6 +73,45 @@ export default function ({ getService }: FtrProviderContext) { // expect redirect expect(status).toBe(302); }); + + it('invalidate', async () => { + const supertestAdmin = await roleScopedSupertest.getSupertestWithRoleScope('admin', { + useCookieHeader: true, + }); + + let body: any; + let status: number; + + ({ body, status } = await supertestViewerWithCookieCredentials + .post('/api/security/session/_invalidate') + .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 [post] exists but is not available with the current configuration' + ), + }); + expect(status).toBe(400); + + ({ body, status } = await supertestViewerWithCookieCredentials + .post('/api/security/session/_invalidate') + .set(samlAuth.getInternalRequestHeader())); + // expect forbidden because the viewer does not have privilege to invalidate a session + expect(status).toBe(403); + + ({ body, status } = await supertestAdmin + .post('/api/security/session/_invalidate') + .set(samlAuth.getInternalRequestHeader())); + // expect 400 due to no body, admin has privilege, but the request body is missing + expect(status).toBe(400); + expect(body).toEqual({ + error: 'Bad Request', + message: '[request body]: expected a plain object value, but found [null] instead.', + statusCode: 400, + }); + }); }); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts index 1bedd0acd0cc4..89edce106f64e 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts @@ -35,7 +35,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { defaultIndex: 'logstash-*', }; - describe('discover esql view', function () { + // Failing: See https://github.com/elastic/kibana/issues/194305 + describe.skip('discover esql view', function () { // see details: https://github.com/elastic/kibana/issues/188816 this.tags(['failsOnMKI']);