[8.7] [Synthetics] only allow params to be saved against specific namespaces (#155759) (#155818)

# Backport

This will backport the following commits from `main` to `8.7`:
- [[Synthetics] only allow params to be saved against specific
namespaces (#155759)](https://github.com/elastic/kibana/pull/155759)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Dominique
Clarke","email":"dominique.clarke@elastic.co"},"sourceCommit":{"committedDate":"2023-04-26T02:10:41Z","message":"[Synthetics]
only allow params to be saved against specific namespaces
(#155759)\n\n## Summary\r\n\r\nThis PR prevents saving synthetics params
to arbitrary Kibana spaces.\r\n\r\nPreviously, we accepted a
`namespaces` array which could be used to save\r\na param to spaces that
do not exist. In the past, the client would send\r\nthis value as
`namespaces: ['*']` when a param is meant to e shared\r\nacross
spaces.\r\n\r\nThis PR replaces the `namespaces` array with a
simple\r\n`share_across_spaces` boolean.\r\n\r\nParams are now only
allowed to be saved to the current active space,
or\r\n`*`.\r\n\r\n---------\r\n\r\nCo-authored-by: shahzad31
<shahzad31comp@gmail.com>","sha":"ead18ac43742edb0406803985eab6b563702b320","branchLabelMapping":{"^v8.8.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:fix","Team:uptime","uptime","ci:cloud-deploy","v8.8.0","v8.7.1"],"number":155759,"url":"https://github.com/elastic/kibana/pull/155759","mergeCommit":{"message":"[Synthetics]
only allow params to be saved against specific namespaces
(#155759)\n\n## Summary\r\n\r\nThis PR prevents saving synthetics params
to arbitrary Kibana spaces.\r\n\r\nPreviously, we accepted a
`namespaces` array which could be used to save\r\na param to spaces that
do not exist. In the past, the client would send\r\nthis value as
`namespaces: ['*']` when a param is meant to e shared\r\nacross
spaces.\r\n\r\nThis PR replaces the `namespaces` array with a
simple\r\n`share_across_spaces` boolean.\r\n\r\nParams are now only
allowed to be saved to the current active space,
or\r\n`*`.\r\n\r\n---------\r\n\r\nCo-authored-by: shahzad31
<shahzad31comp@gmail.com>","sha":"ead18ac43742edb0406803985eab6b563702b320"}},"sourceBranch":"main","suggestedTargetBranches":["8.7"],"targetPullRequestStates":[{"branch":"main","label":"v8.8.0","labelRegex":"^v8.8.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/155759","number":155759,"mergeCommit":{"message":"[Synthetics]
only allow params to be saved against specific namespaces
(#155759)\n\n## Summary\r\n\r\nThis PR prevents saving synthetics params
to arbitrary Kibana spaces.\r\n\r\nPreviously, we accepted a
`namespaces` array which could be used to save\r\na param to spaces that
do not exist. In the past, the client would send\r\nthis value as
`namespaces: ['*']` when a param is meant to e shared\r\nacross
spaces.\r\n\r\nThis PR replaces the `namespaces` array with a
simple\r\n`share_across_spaces` boolean.\r\n\r\nParams are now only
allowed to be saved to the current active space,
or\r\n`*`.\r\n\r\n---------\r\n\r\nCo-authored-by: shahzad31
<shahzad31comp@gmail.com>","sha":"ead18ac43742edb0406803985eab6b563702b320"}},{"branch":"8.7","label":"v8.7.1","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Dominique Clarke 2023-04-25 23:23:35 -04:00 committed by GitHub
parent 01746042a3
commit efcababbe5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 406 additions and 57 deletions

View file

@ -28,6 +28,8 @@ export type {
export type { AuthenticationServiceStart, AuthenticationServiceSetup } from './authentication';
export { ALL_SPACES_ID } from '../common/constants';
export const plugin: PluginInitializer<
SecurityPluginSetup,
SecurityPluginStart,

View file

@ -7,7 +7,7 @@
import * as t from 'io-ts';
export const SyntheticsParamCode = t.intersection([
export const SyntheticsParamSOCodec = t.intersection([
t.interface({
key: t.string,
value: t.string,
@ -19,4 +19,18 @@ export const SyntheticsParamCode = t.intersection([
}),
]);
export type SyntheticsParam = t.TypeOf<typeof SyntheticsParamCode>;
export type SyntheticsParamSO = t.TypeOf<typeof SyntheticsParamSOCodec>;
export const SyntheticsParamRequestCodec = t.intersection([
t.interface({
key: t.string,
value: t.string,
}),
t.partial({
description: t.string,
tags: t.array(t.string),
share_across_spaces: t.boolean,
}),
]);
export type SyntheticsParamRequest = t.TypeOf<typeof SyntheticsParamRequestCodec>;

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import React, { useCallback, useEffect, useState } from 'react';
import { ALL_SPACES_ID } from '@kbn/security-plugin/public';
import {
EuiFlyout,
EuiFlyoutBody,
@ -25,7 +26,7 @@ import { useDispatch } from 'react-redux';
import { apiService } from '../../../../../utils/api_service';
import { ClientPluginsStart } from '../../../../../plugin';
import { ListParamItem } from './params_list';
import { SyntheticsParam } from '../../../../../../common/runtime_types';
import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { useFormWrapped } from '../../../../../hooks/use_form_wrapped';
import { AddParamForm } from './add_param_form';
import { SYNTHETICS_API_URLS } from '../../../../../../common/constants';
@ -46,7 +47,7 @@ export const AddParamFlyout = ({
const { id, ...dataToSave } = isEditingItem ?? {};
const form = useFormWrapped<SyntheticsParam>({
const form = useFormWrapped<SyntheticsParamSO>({
mode: 'onSubmit',
reValidateMode: 'onChange',
shouldFocusError: true,
@ -66,7 +67,7 @@ export const AddParamFlyout = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setIsEditingItem]);
const [paramData, setParamData] = useState<SyntheticsParam | null>(null);
const [paramData, setParamData] = useState<SyntheticsParamSO | null>(null);
const { application } = useKibana<ClientPluginsStart>().services;
@ -74,15 +75,24 @@ export const AddParamFlyout = ({
if (!paramData) {
return;
}
const { namespaces, ...paramRequest } = paramData;
const shareAcrossSpaces = namespaces?.includes(ALL_SPACES_ID);
if (isEditingItem) {
return apiService.put(SYNTHETICS_API_URLS.PARAMS, { id, ...paramData });
return apiService.put(SYNTHETICS_API_URLS.PARAMS, {
id,
...paramRequest,
share_across_spaces: shareAcrossSpaces,
});
}
return apiService.post(SYNTHETICS_API_URLS.PARAMS, paramData);
return apiService.post(SYNTHETICS_API_URLS.PARAMS, {
...paramRequest,
share_across_spaces: shareAcrossSpaces,
});
}, [paramData]);
const canSave = (application?.capabilities.uptime.save ?? false) as boolean;
const onSubmit = (formData: SyntheticsParam) => {
const onSubmit = (formData: SyntheticsParamSO) => {
setParamData(formData);
};

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import React from 'react';
import { ALL_SPACES_ID } from '@kbn/security-plugin/public';
import {
EuiCheckbox,
EuiComboBox,
@ -15,7 +16,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Controller, useFormContext, useFormState } from 'react-hook-form';
import { SyntheticsParam } from '../../../../../../common/runtime_types';
import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { ListParamItem } from './params_list';
export const AddParamForm = ({
@ -25,8 +26,8 @@ export const AddParamForm = ({
items: ListParamItem[];
isEditingItem: ListParamItem | null;
}) => {
const { register, control } = useFormContext<SyntheticsParam>();
const { errors } = useFormState<SyntheticsParam>();
const { register, control } = useFormContext<SyntheticsParamSO>();
const { errors } = useFormState<SyntheticsParamSO>();
const tagsList = items.reduce((acc, item) => {
const tags = item.tags || [];
@ -112,7 +113,7 @@ export const AddParamForm = ({
aria-label={NAMESPACES_LABEL}
onChange={(e) => {
if (e.target.checked) {
field.onChange(['*']);
field.onChange([ALL_SPACES_ID]);
} else {
field.onChange([]);
}

View file

@ -24,12 +24,12 @@ import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/bas
import { useDebounce } from 'react-use';
import { TableTitle } from '../../common/components/table_title';
import { ParamsText } from './params_text';
import { SyntheticsParam } from '../../../../../../common/runtime_types';
import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { useParamsList } from '../hooks/use_params_list';
import { AddParamFlyout } from './add_param_flyout';
import { DeleteParam } from './delete_param';
export interface ListParamItem extends SyntheticsParam {
export interface ListParamItem extends SyntheticsParamSO {
id: string;
}

View file

@ -8,13 +8,13 @@
import { useFetcher } from '@kbn/observability-plugin/public';
import { SavedObject } from '@kbn/core-saved-objects-common';
import { useMemo } from 'react';
import { SyntheticsParam } from '../../../../../../common/runtime_types';
import { SyntheticsParamSO } from '../../../../../../common/runtime_types';
import { apiService } from '../../../../../utils/api_service';
import { SYNTHETICS_API_URLS } from '../../../../../../common/constants';
export const useParamsList = (lastRefresh: number) => {
const { data, loading } = useFetcher<
Promise<{ data: Array<SavedObject<SyntheticsParam>> }>
Promise<{ data: Array<SavedObject<SyntheticsParamSO>> }>
>(() => {
return apiService.get(SYNTHETICS_API_URLS.PARAMS);
}, [lastRefresh]);

View file

@ -6,7 +6,9 @@
*/
import { schema } from '@kbn/config-schema';
import { SyntheticsParam } from '../../../common/runtime_types';
import { ALL_SPACES_ID } from '@kbn/security-plugin/common/constants';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
import { SyntheticsParamRequest, SyntheticsParamSO } from '../../../common/runtime_types';
import { syntheticsParamType } from '../../../common/types/saved_objects';
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
import { SYNTHETICS_API_URLS } from '../../../common/constants';
@ -14,25 +16,35 @@ import { SYNTHETICS_API_URLS } from '../../../common/constants';
export const addSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({
method: 'POST',
path: SYNTHETICS_API_URLS.PARAMS,
validate: {
body: schema.object({
key: schema.string(),
value: schema.string(),
description: schema.maybe(schema.string()),
tags: schema.maybe(schema.arrayOf(schema.string())),
namespaces: schema.maybe(schema.arrayOf(schema.string())),
share_across_spaces: schema.maybe(schema.boolean()),
}),
},
writeAccess: true,
handler: async ({ request, server, savedObjectsClient }): Promise<any> => {
const { namespaces, ...data } = request.body as SyntheticsParam;
const { id: spaceId } = await server.spaces.spacesService.getActiveSpace(request);
handler: async ({ request, response, server, savedObjectsClient }): Promise<any> => {
try {
const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? {
id: DEFAULT_SPACE_ID,
};
const { share_across_spaces: shareAcrossSpaces, ...data } =
request.body as SyntheticsParamRequest;
const result = await savedObjectsClient.create(syntheticsParamType, data, {
initialNamespaces: (namespaces ?? []).length > 0 ? namespaces : [spaceId],
});
const result = await savedObjectsClient.create<SyntheticsParamSO>(syntheticsParamType, data, {
initialNamespaces: shareAcrossSpaces ? [ALL_SPACES_ID] : [spaceId],
});
return { data: result };
} catch (error) {
if (error.output?.statusCode === 404) {
const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } });
}
return { data: result };
throw error;
}
},
});

View file

@ -6,7 +6,8 @@
*/
import { schema } from '@kbn/config-schema';
import { SyntheticsParam } from '../../../common/runtime_types';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
import { SyntheticsParamRequest } from '../../../common/runtime_types';
import { syntheticsParamType } from '../../../common/types/saved_objects';
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
import { SYNTHETICS_API_URLS } from '../../../common/constants';
@ -21,15 +22,33 @@ export const editSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({
value: schema.string(),
description: schema.maybe(schema.string()),
tags: schema.maybe(schema.arrayOf(schema.string())),
namespaces: schema.maybe(schema.arrayOf(schema.string())),
share_across_spaces: schema.maybe(schema.boolean()),
}),
},
writeAccess: true,
handler: async ({ savedObjectsClient, request, server }): Promise<any> => {
const { namespaces, id, ...data } = request.body as SyntheticsParam & { id: string };
handler: async ({ savedObjectsClient, request, response, server }): Promise<any> => {
try {
const { id: _spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? {
id: DEFAULT_SPACE_ID,
};
const {
share_across_spaces: shareAcrossSpaces,
id,
...data
} = request.body as SyntheticsParamRequest & {
id: string;
};
const result = await savedObjectsClient.update(syntheticsParamType, id, data);
const result = await savedObjectsClient.update(syntheticsParamType, id, data);
return { data: result };
return { data: result };
} catch (error) {
if (error.output?.statusCode === 404) {
const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } });
}
throw error;
}
},
});

View file

@ -6,6 +6,7 @@
*/
import { SavedObjectsFindResult } from '@kbn/core-saved-objects-api-server';
import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common';
import { syntheticsParamType } from '../../../common/types/saved_objects';
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
import { SYNTHETICS_API_URLS } from '../../../common/constants';
@ -14,35 +15,46 @@ export const getSyntheticsParamsRoute: SyntheticsRestApiRouteFactory = () => ({
method: 'GET',
path: SYNTHETICS_API_URLS.PARAMS,
validate: {},
handler: async ({ savedObjectsClient, request, server }): Promise<any> => {
const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
handler: async ({ savedObjectsClient, request, response, server }): Promise<any> => {
try {
const encryptedSavedObjectsClient = server.encryptedSavedObjects.getClient();
const spaceId = server.spaces.spacesService.getSpaceId(request);
const { id: spaceId } = (await server.spaces?.spacesService.getActiveSpace(request)) ?? {
id: DEFAULT_SPACE_ID,
};
const canSave =
(await server.coreStart?.capabilities.resolveCapabilities(request)).uptime.save ?? false;
const canSave =
(await server.coreStart?.capabilities.resolveCapabilities(request)).uptime.save ?? false;
if (canSave) {
const finder =
await encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser({
if (canSave) {
const finder =
await encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser({
type: syntheticsParamType,
perPage: 1000,
namespaces: [spaceId],
});
const hits: SavedObjectsFindResult[] = [];
for await (const result of finder.find()) {
hits.push(...result.saved_objects);
}
return { data: hits };
} else {
const data = await savedObjectsClient.find({
type: syntheticsParamType,
perPage: 1000,
namespaces: [spaceId],
perPage: 10000,
});
const hits: SavedObjectsFindResult[] = [];
for await (const result of finder.find()) {
hits.push(...result.saved_objects);
return { data: data.saved_objects };
}
} catch (error) {
if (error.output?.statusCode === 404) {
const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID;
return response.notFound({ body: { message: `Kibana space '${spaceId}' does not exist` } });
}
return { data: hits };
} else {
const data = await savedObjectsClient.find({
type: syntheticsParamType,
perPage: 10000,
});
return { data: data.saved_objects };
throw error;
}
},
});

View file

@ -35,7 +35,7 @@ import {
SyntheticsMonitor,
SyntheticsMonitorWithId,
SyntheticsMonitorWithSecrets,
SyntheticsParam,
SyntheticsParamSO,
ThrottlingOptions,
} from '../../common/runtime_types';
import { getServiceLocations } from './get_service_locations';
@ -476,7 +476,7 @@ export class SyntheticsService {
const paramsBySpace: Record<string, Record<string, string>> = {};
const finder =
await encryptedClient.createPointInTimeFinderDecryptedAsInternalUser<SyntheticsParam>({
await encryptedClient.createPointInTimeFinderDecryptedAsInternalUser<SyntheticsParamSO>({
type: syntheticsParamType,
perPage: 1000,
namespaces: spaceId ? [spaceId] : undefined,

View file

@ -0,0 +1,278 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { v4 as uuidv4 } from 'uuid';
import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
import expect from '@kbn/expect';
import { syntheticsParamType } from '@kbn/synthetics-plugin/common/types/saved_objects';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
describe('AddEditParams', function () {
this.tags('skipCloud');
const supertestAPI = getService('supertest');
const kServer = getService('kibanaServer');
const testParam = {
key: 'test',
value: 'test',
};
before(async () => {
await supertestAPI.post('/api/fleet/setup').set('kbn-xsrf', 'true').send().expect(200);
await supertestAPI
.post('/api/fleet/epm/packages/synthetics/0.12.0')
.set('kbn-xsrf', 'true')
.send({ force: true })
.expect(200);
await kServer.savedObjects.clean({ types: [syntheticsParamType] });
});
it('adds a test param', async () => {
await supertestAPI
.post(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.send(testParam)
.expect(200);
const getResponse = await supertestAPI
.get(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.expect(200);
expect(getResponse.body.data[0].attributes).eql(testParam);
});
it('handles tags and description', async () => {
const tagsAndDescription = {
tags: ['a tag'],
description: 'test description',
};
const testParam2 = {
...testParam,
...tagsAndDescription,
};
await supertestAPI
.post(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.send(testParam2)
.expect(200);
const getResponse = await supertestAPI
.get(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.expect(200);
expect(getResponse.body.data[0].attributes).eql(testParam2);
});
it('handles editing a param', async () => {
const updatedParam = {
key: 'testUpdated',
value: 'testUpdated',
tags: ['a tag'],
description: 'test description',
};
await supertestAPI
.post(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.send(testParam)
.expect(200);
const getResponse = await supertestAPI
.get(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.expect(200);
const param = getResponse.body.data[0];
expect(param.attributes).eql(testParam);
await supertestAPI
.put(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.send({ ...updatedParam, id: param.id })
.expect(200);
const updatedGetResponse = await supertestAPI
.get(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.expect(200);
const updatedParamSO = updatedGetResponse.body.data[0];
expect(updatedParamSO.attributes).eql(updatedParam);
});
it('handles spaces', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
await supertestAPI
.post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send(testParam)
.expect(200);
const getResponse = await supertestAPI
.get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.expect(200);
expect(getResponse.body.data[0].namespaces).eql([SPACE_ID]);
expect(getResponse.body.data[0].attributes).eql(testParam);
});
it('handles editing a param in spaces', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
const updatedParam = {
key: 'testUpdated',
value: 'testUpdated',
tags: ['a tag'],
description: 'test description',
};
await supertestAPI
.post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send(testParam)
.expect(200);
const getResponse = await supertestAPI
.get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.expect(200);
const param = getResponse.body.data[0];
expect(param.attributes).eql(testParam);
await supertestAPI
.put(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send({ ...updatedParam, id: param.id })
.expect(200);
const updatedGetResponse = await supertestAPI
.get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.expect(200);
const updatedParamSO = updatedGetResponse.body.data[0];
expect(updatedParamSO.attributes).eql(updatedParam);
});
it('does not allow editing a param in created in one space in a different space', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
const SPACE_ID_TWO = `test-space-${uuidv4()}-two`;
const SPACE_NAME_TWO = `test-space-name ${uuidv4()} 2`;
await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
await kServer.spaces.create({ id: SPACE_ID_TWO, name: SPACE_NAME_TWO });
const updatedParam = {
key: 'testUpdated',
value: 'testUpdated',
tags: ['a tag'],
description: 'test description',
};
await supertestAPI
.post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send(testParam)
.expect(200);
const getResponse = await supertestAPI
.get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.expect(200);
const param = getResponse.body.data[0];
expect(param.attributes).eql(testParam);
// space does exist so get request should be 200
await supertestAPI
.get(`/s/${SPACE_ID_TWO}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.expect(200);
await supertestAPI
.put(`/s/${SPACE_ID_TWO}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send({ ...updatedParam, id: param.id })
.expect(404);
const updatedGetResponse = await supertestAPI
.get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.expect(200);
const updatedParamSO = updatedGetResponse.body.data[0];
expect(updatedParamSO.attributes).eql(testParam);
});
it('handles invalid spaces', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
await supertestAPI
.post(`/s/doesnotexist${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send(testParam)
.expect(404);
});
it('handles editing with invalid spaces', async () => {
const updatedParam = {
key: 'testUpdated',
value: 'testUpdated',
tags: ['a tag'],
description: 'test description',
};
await supertestAPI
.post(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.send(testParam)
.expect(200);
const getResponse = await supertestAPI
.get(SYNTHETICS_API_URLS.PARAMS)
.set('kbn-xsrf', 'true')
.expect(200);
const param = getResponse.body.data[0];
expect(param.attributes).eql(testParam);
await supertestAPI
.put(`/s/doesnotexist${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send({ ...updatedParam, id: param.id })
.expect(404);
});
it('handles share across spaces', async () => {
const SPACE_ID = `test-space-${uuidv4()}`;
const SPACE_NAME = `test-space-name ${uuidv4()}`;
await kServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME });
await supertestAPI
.post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.send({ ...testParam, share_across_spaces: true })
.expect(200);
const getResponse = await supertestAPI
.get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.PARAMS}`)
.set('kbn-xsrf', 'true')
.expect(200);
expect(getResponse.body.data[0].namespaces).eql(['*']);
expect(getResponse.body.data[0].attributes).eql(testParam);
});
});
}

View file

@ -29,5 +29,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./delete_monitor_project'));
loadTestFile(require.resolve('./synthetics_enablement'));
loadTestFile(require.resolve('./sync_global_params'));
loadTestFile(require.resolve('./add_edit_params'));
});
}

View file

@ -7,7 +7,7 @@
import {
ConfigKey,
HTTPFields,
SyntheticsParam,
SyntheticsParamSO,
} from '@kbn/synthetics-plugin/common/runtime_types';
import { API_URLS, SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants';
import { omit } from 'lodash';
@ -151,7 +151,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(apiResponse.status).eql(200);
apiResponse.body.data.forEach(({ attributes }: SavedObject<SyntheticsParam>) => {
apiResponse.body.data.forEach(({ attributes }: SavedObject<SyntheticsParamSO>) => {
params[attributes.key] = attributes.value;
});
});