mirror of
https://github.com/elastic/kibana.git
synced 2025-06-29 03:24:45 -04:00
fix https://github.com/elastic/kibana/issues/192052 ## Summary Internal APIs will be [restricted](https://github.com/elastic/kibana/issues/163654) from public access as of 9.0.0. In non-serverless environments, this breaking change will result in a 400 error if an external request is made to an internal Kibana API (route `access` option as `"internal"` or `"public"`). This PR allows API owners of non-xpack plugins to run their `ftr` API integration tests against the restriction and adds examples of how to handle it. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios Note to reviewers: The header needed to allow access to internal apis shouldn't change your test output, with or without the restriction enabled. ### How to test the changes work: #### Non x-pack: 1. Set `server.restrictInternalApis: true` in `test/common/config.js` 2. Ensure your tests pass #### x-pack: 1. Set `server.restrictInternalApis: true` in `x-pack/test/api_integration/apis/security/config.ts` 2. Ensure the spaces tests pass --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
519 lines
17 KiB
TypeScript
519 lines
17 KiB
TypeScript
/*
|
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
* or more contributor license agreements. Licensed under the "Elastic License
|
|
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
* Public License v 1"; you may not use this file except in compliance with, at
|
|
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
*/
|
|
|
|
import expect from '@kbn/expect';
|
|
import type {
|
|
EventAnnotationGroupSavedObjectAttributes,
|
|
EventAnnotationGroupCreateIn,
|
|
EventAnnotationGroupCreateOut,
|
|
EventAnnotationGroupUpdateIn,
|
|
EventAnnotationGroupSearchIn,
|
|
EventAnnotationGroupSearchOut,
|
|
EventAnnotationGroupGetIn,
|
|
EventAnnotationGroupGetOut,
|
|
EventAnnotationGroupDeleteIn,
|
|
EventAnnotationGroupDeleteOut,
|
|
} from '@kbn/event-annotation-plugin/common';
|
|
import { CONTENT_ID } from '@kbn/event-annotation-plugin/common';
|
|
import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-common';
|
|
import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common';
|
|
import { FtrProviderContext } from '../../ftr_provider_context';
|
|
|
|
const CONTENT_ENDPOINT = '/api/content_management/rpc';
|
|
|
|
const API_VERSION = 1;
|
|
|
|
// IDs come from from loaded archive
|
|
const EXISTING_ID_1 = '46c2a460-4e77-11ee-bb97-116581699678';
|
|
const EXISTING_ID_2 = '425d2760-4e77-11ee-bb97-116581699678';
|
|
const DESCRIPTION_2 = 'i am a description you can search for!';
|
|
const EXISTING_ID_3 = '3905a4d0-4e77-11ee-bb97-116581699678';
|
|
const TAG_ID = '36a8f020-4e77-11ee-bb97-116581699678';
|
|
|
|
const DEFAULT_EVENT_ANNOTATION_GROUP: EventAnnotationGroupSavedObjectAttributes = {
|
|
title: 'a group',
|
|
description: '',
|
|
ignoreGlobalFilters: true,
|
|
dataViewSpec: null,
|
|
annotations: [
|
|
{
|
|
label: 'Event',
|
|
type: 'manual',
|
|
key: {
|
|
type: 'point_in_time',
|
|
timestamp: '2023-08-10T15:00:00.000Z',
|
|
},
|
|
icon: 'triangle',
|
|
id: '499ee351-f541-46e0-b327-b3dcae91aff5',
|
|
},
|
|
],
|
|
};
|
|
|
|
const DEFAULT_REFERENCES = [
|
|
{
|
|
type: 'index-pattern',
|
|
id: '90943e30-9a47-11e8-b64d-95841ca0b247',
|
|
name: 'event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247',
|
|
},
|
|
];
|
|
|
|
export default function ({ getService }: FtrProviderContext) {
|
|
const kibanaServer = getService('kibanaServer');
|
|
const supertest = getService('supertest');
|
|
|
|
describe('group API', () => {
|
|
before(async () => {
|
|
await kibanaServer.savedObjects.clean({ types: [EVENT_ANNOTATION_GROUP_TYPE] });
|
|
|
|
await kibanaServer.importExport.load(
|
|
'test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json'
|
|
);
|
|
});
|
|
|
|
after(async () => {
|
|
await kibanaServer.importExport.unload(
|
|
'test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json'
|
|
);
|
|
|
|
await kibanaServer.savedObjects.clean({ types: [EVENT_ANNOTATION_GROUP_TYPE] });
|
|
});
|
|
|
|
describe('get', () => {
|
|
it(`should retrieve an existing group`, async () => {
|
|
const payload: EventAnnotationGroupGetIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
id: EXISTING_ID_1,
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await supertest
|
|
.post(`${CONTENT_ENDPOINT}/get`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload)
|
|
.expect(200);
|
|
|
|
const result = resp.body.result.result as EventAnnotationGroupGetOut;
|
|
|
|
expect(result.item.id).to.be(EXISTING_ID_1);
|
|
expect(result.meta.outcome).to.be('exactMatch');
|
|
expect(result.item.references.length).to.be(1);
|
|
expect(result.item.attributes).to.eql({
|
|
annotations: [
|
|
{
|
|
filter: {
|
|
language: 'kuery',
|
|
query:
|
|
'agent.keyword : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)" ',
|
|
type: 'kibana_query',
|
|
},
|
|
icon: 'triangle',
|
|
id: 'fdede168-eff1-400f-b106-0f62061f5099',
|
|
key: {
|
|
type: 'point_in_time',
|
|
},
|
|
label: 'Event',
|
|
timeField: 'timestamp',
|
|
type: 'query',
|
|
},
|
|
],
|
|
dataViewSpec: null,
|
|
description: '',
|
|
ignoreGlobalFilters: true,
|
|
title: 'group3',
|
|
});
|
|
});
|
|
|
|
it(`should reject a group that does not exist`, async () => {
|
|
const payload: EventAnnotationGroupGetIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
id: 'does-not-exist',
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await supertest
|
|
.post(`${CONTENT_ENDPOINT}/get`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload)
|
|
.expect(404);
|
|
|
|
expect(resp.body).to.eql({
|
|
error: 'Not Found',
|
|
message: 'Saved object [event-annotation-group/does-not-exist] not found',
|
|
statusCode: 404,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('search', () => {
|
|
const performSearch = (payload: EventAnnotationGroupSearchIn) =>
|
|
supertest
|
|
.post(`${CONTENT_ENDPOINT}/search`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload);
|
|
|
|
it(`should retrieve existing groups`, async () => {
|
|
const payload: EventAnnotationGroupSearchIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
query: {
|
|
limit: 1000,
|
|
tags: {
|
|
included: [],
|
|
excluded: [],
|
|
},
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await performSearch(payload).expect(200);
|
|
|
|
const results = resp.body.result.result.hits;
|
|
expect(results.length).to.be(3);
|
|
expect(results.map(({ id }: { id: string }) => id)).to.eql([
|
|
EXISTING_ID_1,
|
|
EXISTING_ID_2,
|
|
EXISTING_ID_3,
|
|
]);
|
|
});
|
|
|
|
it(`should filter by tag`, async () => {
|
|
const payload: EventAnnotationGroupSearchIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
query: {
|
|
limit: 1000,
|
|
tags: {
|
|
included: [TAG_ID],
|
|
excluded: [],
|
|
},
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await performSearch(payload).expect(200);
|
|
|
|
const result = resp.body.result.result as EventAnnotationGroupSearchOut;
|
|
expect(result.hits.length).to.be(2);
|
|
expect(
|
|
result.hits.every(({ references }) => references.map(({ id }) => id).includes(TAG_ID))
|
|
).to.be(true);
|
|
});
|
|
|
|
it(`should filter by text`, async () => {
|
|
const payload: EventAnnotationGroupSearchIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
query: {
|
|
limit: 1000,
|
|
text: DESCRIPTION_2,
|
|
tags: {
|
|
included: [],
|
|
excluded: [],
|
|
},
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await performSearch(payload).expect(200);
|
|
|
|
const result = resp.body.result.result as EventAnnotationGroupSearchOut;
|
|
expect(result.hits.length).to.be(1);
|
|
expect(result.hits[0].id).to.be(EXISTING_ID_2);
|
|
});
|
|
|
|
it(`should paginate`, async () => {
|
|
const payload: EventAnnotationGroupSearchIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
query: {
|
|
limit: 1,
|
|
cursor: '1',
|
|
tags: {
|
|
included: [],
|
|
excluded: [],
|
|
},
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await performSearch(payload).expect(200);
|
|
|
|
const result = resp.body.result.result as EventAnnotationGroupSearchOut;
|
|
expect(result.hits.length).to.be(1);
|
|
expect(result.hits[0].id).to.be(EXISTING_ID_1);
|
|
expect(result.pagination.total).to.be(3);
|
|
|
|
// get second page
|
|
payload.query.cursor = '2';
|
|
|
|
const resp2 = await performSearch(payload).expect(200);
|
|
|
|
const result2 = resp2.body.result.result as EventAnnotationGroupSearchOut;
|
|
expect(result2.hits.length).to.be(1);
|
|
expect(result2.hits[0].id).to.be(EXISTING_ID_2);
|
|
expect(result2.pagination.total).to.be(3);
|
|
|
|
// get third page
|
|
payload.query.cursor = '3';
|
|
|
|
const resp3 = await performSearch(payload).expect(200);
|
|
|
|
const result3 = resp3.body.result.result as EventAnnotationGroupSearchOut;
|
|
expect(result3.hits.length).to.be(1);
|
|
expect(result3.hits[0].id).to.be(EXISTING_ID_3);
|
|
expect(result3.pagination.total).to.be(3);
|
|
});
|
|
});
|
|
|
|
describe('create', () => {
|
|
it(`should create a new group`, async () => {
|
|
const payload: EventAnnotationGroupCreateIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
data: DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
options: {
|
|
references: DEFAULT_REFERENCES,
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await supertest
|
|
.post(`${CONTENT_ENDPOINT}/create`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload)
|
|
.expect(200);
|
|
|
|
const result = resp.body.result.result as EventAnnotationGroupCreateOut;
|
|
|
|
expect(result.item.attributes).to.eql(DEFAULT_EVENT_ANNOTATION_GROUP);
|
|
expect(result.item.id).to.be.a('string');
|
|
expect(result.item.namespaces).to.eql(['default']);
|
|
});
|
|
|
|
it(`should reject malformed groups`, async () => {
|
|
const badGroups = [
|
|
// extra property
|
|
{
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
extraProp: 'some-value',
|
|
},
|
|
// missing title
|
|
{
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
title: undefined,
|
|
},
|
|
// wrong type for property
|
|
{
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
ignoreGlobalFilters: 'not-a-boolean',
|
|
},
|
|
] as unknown as EventAnnotationGroupSavedObjectAttributes[]; // (coerce the types because these are intentionally malformed)
|
|
|
|
const expectedMessages = [
|
|
'Invalid data. [extraProp]: definition for this key is missing',
|
|
'Invalid data. [title]: expected value of type [string] but got [undefined]',
|
|
'Invalid data. [ignoreGlobalFilters]: expected value of type [boolean] but got [string]',
|
|
];
|
|
|
|
for (let i = 0; i < badGroups.length; i++) {
|
|
const payload: EventAnnotationGroupCreateIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
data: badGroups[i],
|
|
options: {
|
|
references: DEFAULT_REFERENCES,
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await supertest
|
|
.post(`${CONTENT_ENDPOINT}/create`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload)
|
|
.expect(400);
|
|
|
|
expect(resp.body.message).to.be(expectedMessages[i]);
|
|
}
|
|
});
|
|
|
|
it(`should require dataViewSpec to be specified`, async () => {
|
|
const createWithDataViewSpec = (dataViewSpec: any) => {
|
|
const payload: EventAnnotationGroupCreateIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
data: { ...DEFAULT_EVENT_ANNOTATION_GROUP, dataViewSpec },
|
|
options: {
|
|
references: DEFAULT_REFERENCES,
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
return supertest
|
|
.post(`${CONTENT_ENDPOINT}/create`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload);
|
|
};
|
|
|
|
const errorResp = await createWithDataViewSpec(undefined).expect(400);
|
|
|
|
expect(errorResp.body.message).to.be(
|
|
'Invalid data. [dataViewSpec]: expected at least one defined value but got [undefined]'
|
|
);
|
|
|
|
await createWithDataViewSpec(null).expect(200);
|
|
|
|
await createWithDataViewSpec({
|
|
someDataViewProp: 'some-value',
|
|
}).expect(200);
|
|
});
|
|
});
|
|
|
|
describe('update', () => {
|
|
it(`should update a group`, async () => {
|
|
const payload: EventAnnotationGroupUpdateIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
data: {
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
description: 'updated description',
|
|
},
|
|
id: EXISTING_ID_1,
|
|
options: {
|
|
references: DEFAULT_REFERENCES,
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await supertest
|
|
.post(`${CONTENT_ENDPOINT}/update`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload)
|
|
.expect(200);
|
|
|
|
const result = resp.body.result.result as EventAnnotationGroupCreateOut;
|
|
|
|
expect(result.item.attributes).to.eql({
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
description: 'updated description',
|
|
});
|
|
expect(result.item.id).to.be(EXISTING_ID_1);
|
|
});
|
|
|
|
it(`should reject malformed groups`, async () => {
|
|
const badGroups = [
|
|
// extra property
|
|
{
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
extraProp: 'some-value',
|
|
},
|
|
// missing title
|
|
{
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
title: undefined,
|
|
},
|
|
// wrong type for property
|
|
{
|
|
...DEFAULT_EVENT_ANNOTATION_GROUP,
|
|
ignoreGlobalFilters: 'not-a-boolean',
|
|
},
|
|
] as unknown as EventAnnotationGroupSavedObjectAttributes[]; // (coerce the types because these are intentionally malformed)
|
|
|
|
const expectedMessages = [
|
|
'Invalid data. [extraProp]: definition for this key is missing',
|
|
'Invalid data. [title]: expected value of type [string] but got [undefined]',
|
|
'Invalid data. [ignoreGlobalFilters]: expected value of type [boolean] but got [string]',
|
|
];
|
|
|
|
for (let i = 0; i < badGroups.length; i++) {
|
|
const payload: EventAnnotationGroupUpdateIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
data: badGroups[i],
|
|
id: EXISTING_ID_1,
|
|
options: {
|
|
references: DEFAULT_REFERENCES,
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
const resp = await supertest
|
|
.post(`${CONTENT_ENDPOINT}/update`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload)
|
|
.expect(400);
|
|
|
|
expect(resp.body.message).to.be(expectedMessages[i]);
|
|
}
|
|
});
|
|
|
|
it(`should require dataViewSpec to be specified`, async () => {
|
|
const updateWithDataViewSpec = (dataViewSpec: any) => {
|
|
const payload: EventAnnotationGroupUpdateIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
data: { ...DEFAULT_EVENT_ANNOTATION_GROUP, dataViewSpec },
|
|
id: EXISTING_ID_1,
|
|
options: {
|
|
references: DEFAULT_REFERENCES,
|
|
},
|
|
version: API_VERSION,
|
|
};
|
|
|
|
return supertest
|
|
.post(`${CONTENT_ENDPOINT}/update`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload);
|
|
};
|
|
const errorResp = await updateWithDataViewSpec(undefined).expect(400);
|
|
|
|
expect(errorResp.body.message).to.be(
|
|
'Invalid data. [dataViewSpec]: expected at least one defined value but got [undefined]'
|
|
);
|
|
|
|
await updateWithDataViewSpec(null).expect(200);
|
|
|
|
await updateWithDataViewSpec({
|
|
someDataViewProp: 'some-value',
|
|
}).expect(200);
|
|
});
|
|
});
|
|
|
|
describe('delete', () => {
|
|
const deleteGroupByID = (id: string) => {
|
|
const payload: EventAnnotationGroupDeleteIn = {
|
|
contentTypeId: CONTENT_ID,
|
|
id,
|
|
version: API_VERSION,
|
|
};
|
|
|
|
return supertest
|
|
.post(`${CONTENT_ENDPOINT}/delete`)
|
|
.set('kbn-xsrf', 'kibana')
|
|
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
|
|
.send(payload);
|
|
};
|
|
|
|
it(`should delete a group`, async () => {
|
|
const resp = await deleteGroupByID(EXISTING_ID_1).expect(200);
|
|
|
|
const result = resp.body.result.result as EventAnnotationGroupDeleteOut;
|
|
|
|
expect(result.success).to.be(true);
|
|
});
|
|
|
|
it(`should reject deleting a group that does not exist`, async () => {
|
|
const resp = await deleteGroupByID('does-not-exist').expect(404);
|
|
|
|
expect(resp.body).to.eql({
|
|
error: 'Not Found',
|
|
message: 'Saved object [event-annotation-group/does-not-exist] not found',
|
|
statusCode: 404,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|