mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Cases] Remove case id from alerts when deleting a case (#154829)
## Summary This PR removes the case id from all alerts attached to a case when deleting a case. It also removes any alert authorization when removing alerts from a case. ### Checklist Delete any items that are not applicable to this PR. - [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 ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
136876091a
commit
42effff78d
16 changed files with 683 additions and 181 deletions
|
@ -43,7 +43,7 @@ export interface UpdateAlertCasesRequest {
|
|||
caseIds: string[];
|
||||
}
|
||||
|
||||
export interface RemoveAlertsFromCaseRequest {
|
||||
export interface RemoveCaseIdFromAlertsRequest {
|
||||
alerts: AlertInfo[];
|
||||
caseId: string;
|
||||
}
|
||||
|
|
|
@ -25,10 +25,6 @@ describe('delete', () => {
|
|||
it('delete alerts correctly', async () => {
|
||||
await deleteComment({ caseID: 'mock-id-4', attachmentID: 'mock-comment-4' }, clientArgs);
|
||||
|
||||
expect(clientArgs.services.alertsService.ensureAlertsAuthorized).toHaveBeenCalledWith({
|
||||
alerts: [{ id: 'test-id', index: 'test-index' }],
|
||||
});
|
||||
|
||||
expect(clientArgs.services.alertsService.removeCaseIdFromAlerts).toHaveBeenCalledWith({
|
||||
alerts: [{ id: 'test-id', index: 'test-index' }],
|
||||
caseId: 'mock-id-4',
|
||||
|
@ -39,7 +35,6 @@ describe('delete', () => {
|
|||
clientArgs.services.attachmentService.getter.get.mockResolvedValue(commentSO);
|
||||
await deleteComment({ caseID: 'mock-id-1', attachmentID: 'mock-comment-1' }, clientArgs);
|
||||
|
||||
expect(clientArgs.services.alertsService.ensureAlertsAuthorized).not.toHaveBeenCalledWith();
|
||||
expect(clientArgs.services.alertsService.removeCaseIdFromAlerts).not.toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
@ -66,14 +61,6 @@ describe('delete', () => {
|
|||
it('delete alerts correctly', async () => {
|
||||
await deleteAll({ caseID: 'mock-id-4' }, clientArgs);
|
||||
|
||||
expect(clientArgs.services.alertsService.ensureAlertsAuthorized).toHaveBeenCalledWith({
|
||||
alerts: [
|
||||
{ id: 'test-id', index: 'test-index' },
|
||||
{ id: 'test-id-2', index: 'test-index-2' },
|
||||
{ id: 'test-id-3', index: 'test-index-3' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(clientArgs.services.alertsService.removeCaseIdFromAlerts).toHaveBeenCalledWith({
|
||||
alerts: [
|
||||
{ id: 'test-id', index: 'test-index' },
|
||||
|
|
|
@ -151,6 +151,5 @@ const handleAlerts = async ({ alertsService, attachments, caseId }: HandleAlerts
|
|||
}
|
||||
|
||||
const alerts = getAlertInfoFromComments(alertAttachments);
|
||||
await alertsService.ensureAlertsAuthorized({ alerts });
|
||||
await alertsService.removeCaseIdFromAlerts({ alerts, caseId });
|
||||
};
|
||||
|
|
|
@ -10,11 +10,14 @@ import { createFileServiceMock } from '@kbn/files-plugin/server/mocks';
|
|||
import type { FileJSON } from '@kbn/shared-ux-file-types';
|
||||
import type { CaseFileMetadataForDeletion } from '../../../common/files';
|
||||
import { constructFileKindIdByOwner } from '../../../common/files';
|
||||
import { getFileEntities } from './delete';
|
||||
import { getFileEntities, deleteCases } from './delete';
|
||||
import { createCasesClientMockArgs } from '../mocks';
|
||||
import { mockCases } from '../../mocks';
|
||||
|
||||
const getCaseIds = (numIds: number) => {
|
||||
return Array.from(Array(numIds).keys()).map((key) => key.toString());
|
||||
};
|
||||
|
||||
describe('delete', () => {
|
||||
describe('getFileEntities', () => {
|
||||
const numCaseIds = 1000;
|
||||
|
@ -66,6 +69,33 @@ describe('delete', () => {
|
|||
expect(entities).toEqual(expectedEntities);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteCases', () => {
|
||||
const clientArgs = createCasesClientMockArgs();
|
||||
clientArgs.fileService.find.mockResolvedValue({ files: [], total: 0 });
|
||||
|
||||
clientArgs.services.caseService.getCases.mockResolvedValue({
|
||||
saved_objects: [mockCases[0], mockCases[1]],
|
||||
});
|
||||
|
||||
clientArgs.services.attachmentService.getter.getAttachmentIdsForCases.mockResolvedValue([]);
|
||||
clientArgs.services.userActionService.getUserActionIdsForCases.mockResolvedValue([]);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('alerts', () => {
|
||||
const caseIds = ['mock-id-1', 'mock-id-2'];
|
||||
|
||||
it('removes the case ids from all alerts', async () => {
|
||||
await deleteCases(caseIds, clientArgs);
|
||||
expect(clientArgs.services.alertsService.removeCaseIdsFromAllAlerts).toHaveBeenCalledWith({
|
||||
caseIds,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const createMockFileJSON = (): FileJSON => {
|
||||
|
|
|
@ -28,7 +28,7 @@ import { createFileEntities, deleteFiles } from '../files';
|
|||
*/
|
||||
export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): Promise<void> {
|
||||
const {
|
||||
services: { caseService, attachmentService, userActionService },
|
||||
services: { caseService, attachmentService, userActionService, alertsService },
|
||||
logger,
|
||||
authorization,
|
||||
fileService,
|
||||
|
@ -75,6 +75,7 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P
|
|||
entities: bulkDeleteEntities,
|
||||
options: { refresh: 'wait_for' },
|
||||
}),
|
||||
alertsService.removeCaseIdsFromAllAlerts({ caseIds: ids }),
|
||||
]);
|
||||
|
||||
await userActionService.creator.bulkAuditLogCaseDeletion(
|
||||
|
|
|
@ -423,5 +423,47 @@ describe('updateAlertsStatus', () => {
|
|||
|
||||
expect(alertsClient.removeCaseIdFromAlerts).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not throw an error and log it', async () => {
|
||||
alertsClient.removeCaseIdFromAlerts.mockRejectedValueOnce('An error');
|
||||
|
||||
await expect(
|
||||
alertService.removeCaseIdFromAlerts({ alerts, caseId })
|
||||
).resolves.not.toThrowError();
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
'Failed removing case test-case from alerts: An error'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeCaseIdsFromAllAlerts', () => {
|
||||
const caseIds = ['test-case-1', 'test-case-2'];
|
||||
|
||||
it('remove all case ids from alerts', async () => {
|
||||
await alertService.removeCaseIdsFromAllAlerts({ caseIds });
|
||||
|
||||
expect(alertsClient.removeCaseIdsFromAllAlerts).toBeCalledWith({ caseIds });
|
||||
});
|
||||
|
||||
it('does not call the alerts client with no case ids', async () => {
|
||||
await alertService.removeCaseIdsFromAllAlerts({
|
||||
caseIds: [],
|
||||
});
|
||||
|
||||
expect(alertsClient.removeCaseIdsFromAllAlerts).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not throw an error and log it', async () => {
|
||||
alertsClient.removeCaseIdsFromAllAlerts.mockRejectedValueOnce('An error');
|
||||
|
||||
await expect(
|
||||
alertService.removeCaseIdsFromAllAlerts({ caseIds })
|
||||
).resolves.not.toThrowError();
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
'Failed removing cases test-case-1,test-case-2 for all alerts: An error'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ import { MAX_ALERTS_PER_CASE, MAX_CONCURRENT_SEARCHES } from '../../../common/co
|
|||
import { createCaseError } from '../../common/error';
|
||||
import type { AlertInfo } from '../../common/types';
|
||||
import type {
|
||||
RemoveAlertsFromCaseRequest,
|
||||
RemoveCaseIdFromAlertsRequest,
|
||||
UpdateAlertCasesRequest,
|
||||
UpdateAlertStatusRequest,
|
||||
} from '../../client/alerts/types';
|
||||
|
@ -237,7 +237,7 @@ export class AlertService {
|
|||
public async removeCaseIdFromAlerts({
|
||||
alerts,
|
||||
caseId,
|
||||
}: RemoveAlertsFromCaseRequest): Promise<void> {
|
||||
}: RemoveCaseIdFromAlertsRequest): Promise<void> {
|
||||
try {
|
||||
const nonEmptyAlerts = this.getNonEmptyAlerts(alerts);
|
||||
|
||||
|
@ -250,11 +250,31 @@ export class AlertService {
|
|||
caseId,
|
||||
});
|
||||
} catch (error) {
|
||||
throw createCaseError({
|
||||
message: `Failed to remove case ${caseId} from alerts: ${error}`,
|
||||
error,
|
||||
logger: this.logger,
|
||||
/**
|
||||
* We intentionally do not throw an error.
|
||||
* Users should be able to remove alerts from a case even
|
||||
* in the event of an error produced by the alerts client
|
||||
*/
|
||||
this.logger.error(`Failed removing case ${caseId} from alerts: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async removeCaseIdsFromAllAlerts({ caseIds }: { caseIds: string[] }): Promise<void> {
|
||||
try {
|
||||
if (caseIds.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.alertsClient.removeCaseIdsFromAllAlerts({
|
||||
caseIds,
|
||||
});
|
||||
} catch (error) {
|
||||
/**
|
||||
* We intentionally do not throw an error.
|
||||
* Users should be able to remove alerts from cases even
|
||||
* in the event of an error produced by the alerts client
|
||||
*/
|
||||
this.logger.error(`Failed removing cases ${caseIds} for all alerts: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ export type LicensingServiceMock = jest.Mocked<LicensingService>;
|
|||
export type NotificationServiceMock = jest.Mocked<EmailNotificationService>;
|
||||
|
||||
export const createCaseServiceMock = (): CaseServiceMock => {
|
||||
const service = {
|
||||
const service: PublicMethodsOf<CaseServiceMock> = {
|
||||
deleteCase: jest.fn(),
|
||||
findCases: jest.fn(),
|
||||
getAllCaseComments: jest.fn(),
|
||||
|
@ -61,6 +61,7 @@ export const createCaseServiceMock = (): CaseServiceMock => {
|
|||
findCasesGroupedByID: jest.fn(),
|
||||
getCaseStatusStats: jest.fn(),
|
||||
executeAggregations: jest.fn(),
|
||||
bulkDeleteCaseEntities: jest.fn(),
|
||||
};
|
||||
|
||||
// the cast here is required because jest.Mocked tries to include private members and would throw an error
|
||||
|
@ -140,6 +141,7 @@ export const createAlertServiceMock = (): AlertServiceMock => {
|
|||
bulkUpdateCases: jest.fn(),
|
||||
ensureAlertsAuthorized: jest.fn(),
|
||||
removeCaseIdFromAlerts: jest.fn(),
|
||||
removeCaseIdsFromAllAlerts: jest.fn(),
|
||||
};
|
||||
|
||||
// the cast here is required because jest.Mocked tries to include private members and would throw an error
|
||||
|
|
|
@ -24,6 +24,7 @@ const createAlertsClientMock = () => {
|
|||
getAlertSummary: jest.fn(),
|
||||
ensureAllAlertsAuthorizedRead: jest.fn(),
|
||||
removeCaseIdFromAlerts: jest.fn(),
|
||||
removeCaseIdsFromAllAlerts: jest.fn(),
|
||||
};
|
||||
return mocked;
|
||||
};
|
||||
|
|
|
@ -105,7 +105,7 @@ export interface BulkUpdateCasesOptions {
|
|||
caseIds: string[];
|
||||
}
|
||||
|
||||
export interface RemoveAlertsFromCaseOptions {
|
||||
export interface RemoveCaseIdFromAlertsOptions {
|
||||
alerts: MgetAndAuditAlert[];
|
||||
caseId: string;
|
||||
}
|
||||
|
@ -851,33 +851,33 @@ export class AlertsClient {
|
|||
});
|
||||
}
|
||||
|
||||
public async removeCaseIdFromAlerts({ caseId, alerts }: RemoveAlertsFromCaseOptions) {
|
||||
public async removeCaseIdFromAlerts({ caseId, alerts }: RemoveCaseIdFromAlertsOptions) {
|
||||
/**
|
||||
* We intentionally do not perform any authorization
|
||||
* on the alerts. Users should be able to remove
|
||||
* cases from alerts when deleting a case or an
|
||||
* attachment
|
||||
*/
|
||||
try {
|
||||
if (alerts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mgetRes = await this.ensureAllAlertsAuthorized({
|
||||
alerts,
|
||||
operation: ReadOperations.Get,
|
||||
});
|
||||
|
||||
const painlessScript = `if (ctx._source['${ALERT_CASE_IDS}'] != null) {
|
||||
for (int i=0; i < ctx._source['${ALERT_CASE_IDS}'].length; i++) {
|
||||
if (ctx._source['${ALERT_CASE_IDS}'][i].equals('${caseId}')) {
|
||||
ctx._source['${ALERT_CASE_IDS}'].remove(i);
|
||||
}
|
||||
if (ctx._source['${ALERT_CASE_IDS}'].contains('${caseId}')) {
|
||||
int index = ctx._source['${ALERT_CASE_IDS}'].indexOf('${caseId}');
|
||||
ctx._source['${ALERT_CASE_IDS}'].remove(index);
|
||||
}
|
||||
}`;
|
||||
|
||||
const bulkUpdateRequest = [];
|
||||
|
||||
for (const doc of mgetRes.docs) {
|
||||
for (const alert of alerts) {
|
||||
bulkUpdateRequest.push(
|
||||
{
|
||||
update: {
|
||||
_index: doc._index,
|
||||
_id: doc._id,
|
||||
_index: alert.index,
|
||||
_id: alert.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -891,11 +891,61 @@ export class AlertsClient {
|
|||
body: bulkUpdateRequest,
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(`Error to remove case ${caseId} from alerts: ${error}`);
|
||||
this.logger.error(`Error removing case ${caseId} from alerts: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async removeCaseIdsFromAllAlerts({ caseIds }: { caseIds: string[] }) {
|
||||
/**
|
||||
* We intentionally do not perform any authorization
|
||||
* on the alerts. Users should be able to remove
|
||||
* cases from alerts when deleting a case or an
|
||||
* attachment
|
||||
*/
|
||||
try {
|
||||
if (caseIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = `${this.ruleDataService.getResourcePrefix()}-*`;
|
||||
const query = `${ALERT_CASE_IDS}: (${caseIds.join(' or ')})`;
|
||||
const esQuery = buildEsQuery(undefined, { query, language: 'kuery' }, []);
|
||||
|
||||
const SCRIPT_PARAMS_ID = 'caseIds';
|
||||
|
||||
const painlessScript = `if (ctx._source['${ALERT_CASE_IDS}'] != null && ctx._source['${ALERT_CASE_IDS}'].length > 0 && params['${SCRIPT_PARAMS_ID}'] != null && params['${SCRIPT_PARAMS_ID}'].length > 0) {
|
||||
List storedCaseIds = ctx._source['${ALERT_CASE_IDS}'];
|
||||
List caseIdsToRemove = params['${SCRIPT_PARAMS_ID}'];
|
||||
|
||||
for (int i=0; i < caseIdsToRemove.length; i++) {
|
||||
if (storedCaseIds.contains(caseIdsToRemove[i])) {
|
||||
int index = storedCaseIds.indexOf(caseIdsToRemove[i]);
|
||||
storedCaseIds.remove(index);
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
await this.esClient.updateByQuery({
|
||||
index,
|
||||
conflicts: 'proceed',
|
||||
refresh: true,
|
||||
body: {
|
||||
script: {
|
||||
source: painlessScript,
|
||||
lang: 'painless',
|
||||
params: { caseIds },
|
||||
} as InlineScript,
|
||||
query: esQuery,
|
||||
},
|
||||
ignore_unavailable: true,
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error(`Failed removing ${caseIds} from all alerts: ${err}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
public async find<Params extends RuleTypeParams = never>({
|
||||
aggs,
|
||||
featureIds,
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* 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 { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
|
||||
import { alertingAuthorizationMock } from '@kbn/alerting-plugin/server/authorization/alerting_authorization.mock';
|
||||
import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks';
|
||||
import { ALERT_CASE_IDS, ALERT_RULE_CONSUMER, ALERT_RULE_TYPE_ID } from '@kbn/rule-data-utils';
|
||||
import { AlertsClient, ConstructorOptions } from '../alerts_client';
|
||||
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
|
||||
describe('removeCaseIdFromAlerts', () => {
|
||||
const alertingAuthMock = alertingAuthorizationMock.create();
|
||||
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
|
||||
const auditLogger = auditLoggerMock.create();
|
||||
const caseId = 'test-case';
|
||||
const alerts = [
|
||||
{
|
||||
id: 'alert-id',
|
||||
index: 'alert-index',
|
||||
},
|
||||
];
|
||||
|
||||
const alertsClientParams: jest.Mocked<ConstructorOptions> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
authorization: alertingAuthMock,
|
||||
esClient: esClientMock,
|
||||
auditLogger,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
esClientMock.mget.mockResponseOnce({
|
||||
docs: [
|
||||
{
|
||||
found: true,
|
||||
_id: 'alert-id',
|
||||
_index: 'alert-index',
|
||||
_source: {
|
||||
[ALERT_RULE_TYPE_ID]: 'apm.error_rate',
|
||||
[ALERT_RULE_CONSUMER]: 'apm',
|
||||
[ALERT_CASE_IDS]: [caseId],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('removes alerts from a case', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
await alertsClient.removeCaseIdFromAlerts({ caseId, alerts });
|
||||
|
||||
expect(esClientMock.mget).toHaveBeenCalledWith({
|
||||
docs: [{ _id: 'alert-id', _index: 'alert-index' }],
|
||||
});
|
||||
|
||||
expect(esClientMock.bulk.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"body": Array [
|
||||
Object {
|
||||
"update": Object {
|
||||
"_id": "alert-id",
|
||||
"_index": "alert-index",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"script": Object {
|
||||
"lang": "painless",
|
||||
"source": "if (ctx._source['kibana.alert.case_ids'] != null) {
|
||||
for (int i=0; i < ctx._source['kibana.alert.case_ids'].length; i++) {
|
||||
if (ctx._source['kibana.alert.case_ids'][i].equals('test-case')) {
|
||||
ctx._source['kibana.alert.case_ids'].remove(i);
|
||||
}
|
||||
}
|
||||
}",
|
||||
},
|
||||
},
|
||||
],
|
||||
"refresh": "wait_for",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('calls ensureAllAlertsAuthorized correctly', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
await alertsClient.removeCaseIdFromAlerts({ caseId, alerts });
|
||||
|
||||
expect(alertingAuthMock.ensureAuthorized).toHaveBeenCalledWith({
|
||||
consumer: 'apm',
|
||||
entity: 'alert',
|
||||
operation: 'get',
|
||||
ruleTypeId: 'apm.error_rate',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not do any calls if there are no alerts', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
await alertsClient.removeCaseIdFromAlerts({ caseId, alerts: [] });
|
||||
|
||||
expect(alertingAuthMock.ensureAuthorized).not.toHaveBeenCalled();
|
||||
expect(esClientMock.bulk).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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 { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
|
||||
import { alertingAuthorizationMock } from '@kbn/alerting-plugin/server/authorization/alerting_authorization.mock';
|
||||
import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks';
|
||||
import { AlertsClient, ConstructorOptions } from '../alerts_client';
|
||||
import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
|
||||
|
||||
describe('remove cases from alerts', () => {
|
||||
describe('removeCaseIdFromAlerts', () => {
|
||||
const alertingAuthMock = alertingAuthorizationMock.create();
|
||||
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
|
||||
const auditLogger = auditLoggerMock.create();
|
||||
const caseId = 'test-case';
|
||||
const alerts = [
|
||||
{
|
||||
id: 'alert-id',
|
||||
index: 'alert-index',
|
||||
},
|
||||
];
|
||||
|
||||
const alertsClientParams: jest.Mocked<ConstructorOptions> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
authorization: alertingAuthMock,
|
||||
esClient: esClientMock,
|
||||
auditLogger,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('removes alerts from a case', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
await alertsClient.removeCaseIdFromAlerts({ caseId, alerts });
|
||||
|
||||
expect(esClientMock.bulk.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"body": Array [
|
||||
Object {
|
||||
"update": Object {
|
||||
"_id": "alert-id",
|
||||
"_index": "alert-index",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"script": Object {
|
||||
"lang": "painless",
|
||||
"source": "if (ctx._source['kibana.alert.case_ids'] != null) {
|
||||
if (ctx._source['kibana.alert.case_ids'].contains('test-case')) {
|
||||
int index = ctx._source['kibana.alert.case_ids'].indexOf('test-case');
|
||||
ctx._source['kibana.alert.case_ids'].remove(index);
|
||||
}
|
||||
}",
|
||||
},
|
||||
},
|
||||
],
|
||||
"refresh": "wait_for",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('does not do any calls if there are no alerts', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
await alertsClient.removeCaseIdFromAlerts({ caseId, alerts: [] });
|
||||
|
||||
expect(esClientMock.bulk).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeCaseIdsFromAllAlerts', () => {
|
||||
const alertingAuthMock = alertingAuthorizationMock.create();
|
||||
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
|
||||
const auditLogger = auditLoggerMock.create();
|
||||
const caseIds = ['test-case-1', 'test-case-2'];
|
||||
|
||||
const alertsClientParams: jest.Mocked<ConstructorOptions> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
authorization: alertingAuthMock,
|
||||
esClient: esClientMock,
|
||||
auditLogger,
|
||||
ruleDataService: ruleDataServiceMock.create(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('removes alerts from a case', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
await alertsClient.removeCaseIdsFromAllAlerts({ caseIds });
|
||||
|
||||
expect(esClientMock.updateByQuery.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"body": Object {
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"match": Object {
|
||||
"kibana.alert.case_ids": "test-case-1",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"match": Object {
|
||||
"kibana.alert.case_ids": "test-case-2",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
"must": Array [],
|
||||
"must_not": Array [],
|
||||
"should": Array [],
|
||||
},
|
||||
},
|
||||
"script": Object {
|
||||
"lang": "painless",
|
||||
"params": Object {
|
||||
"caseIds": Array [
|
||||
"test-case-1",
|
||||
"test-case-2",
|
||||
],
|
||||
},
|
||||
"source": "if (ctx._source['kibana.alert.case_ids'] != null && ctx._source['kibana.alert.case_ids'].length > 0 && params['caseIds'] != null && params['caseIds'].length > 0) {
|
||||
List storedCaseIds = ctx._source['kibana.alert.case_ids'];
|
||||
List caseIdsToRemove = params['caseIds'];
|
||||
|
||||
for (int i=0; i < caseIdsToRemove.length; i++) {
|
||||
if (storedCaseIds.contains(caseIdsToRemove[i])) {
|
||||
int index = storedCaseIds.indexOf(caseIdsToRemove[i]);
|
||||
storedCaseIds.remove(index);
|
||||
}
|
||||
}
|
||||
}",
|
||||
},
|
||||
},
|
||||
"conflicts": "proceed",
|
||||
"ignore_unavailable": true,
|
||||
"index": "undefined-*",
|
||||
"refresh": true,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('does not do any calls if there are no cases', async () => {
|
||||
const alertsClient = new AlertsClient(alertsClientParams);
|
||||
await alertsClient.removeCaseIdsFromAllAlerts({ caseIds: [] });
|
||||
|
||||
expect(esClientMock.updateByQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -12,7 +12,7 @@ import { ToolingLog } from '@kbn/tooling-log';
|
|||
import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { DetectionAlert } from '@kbn/security-solution-plugin/common/detection_engine/schemas/alerts';
|
||||
import { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/enrichments/types';
|
||||
import { CommentType } from '@kbn/cases-plugin/common';
|
||||
import { CaseResponse, CommentType } from '@kbn/cases-plugin/common';
|
||||
import { ALERT_CASE_IDS } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
getRuleForSignalTesting,
|
||||
|
@ -25,7 +25,7 @@ import {
|
|||
import { superUser } from './authentication/users';
|
||||
import { User } from './authentication/types';
|
||||
import { getSpaceUrlPrefix } from './api/helpers';
|
||||
import { createCase } from './api/case';
|
||||
import { createCase, deleteCases } from './api/case';
|
||||
import { createComment, deleteAllComments } from './api';
|
||||
import { postCaseReq } from './mock';
|
||||
|
||||
|
@ -102,6 +102,100 @@ export const createCaseAttachAlertAndDeleteAlert = async ({
|
|||
alerts: Alerts;
|
||||
getAlerts: (alerts: Alerts) => Promise<Array<Record<string, unknown>>>;
|
||||
}) => {
|
||||
const updatedCases = await createCaseAndAttachAlert({
|
||||
supertest,
|
||||
totalCases,
|
||||
owner,
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
|
||||
const caseToDelete = updatedCases[indexOfCaseToDelete];
|
||||
|
||||
await deleteAllComments({
|
||||
supertest,
|
||||
caseId: caseToDelete.id,
|
||||
expectedHttpCode,
|
||||
auth: deleteCommentAuth,
|
||||
});
|
||||
|
||||
const alertAfterDeletion = await getAlerts(alerts);
|
||||
const caseIdsWithoutRemovedCase = getCaseIdsWithoutRemovedCases({
|
||||
expectedHttpCode,
|
||||
updatedCases,
|
||||
caseIdsToDelete: [caseToDelete.id],
|
||||
});
|
||||
|
||||
for (const alert of alertAfterDeletion) {
|
||||
expect(alert[ALERT_CASE_IDS]).eql(caseIdsWithoutRemovedCase);
|
||||
}
|
||||
};
|
||||
|
||||
export const createCaseAttachAlertAndDeleteCase = async ({
|
||||
supertest,
|
||||
totalCases,
|
||||
indicesOfCaseToDelete,
|
||||
owner,
|
||||
expectedHttpCode = 204,
|
||||
deleteCaseAuth = { user: superUser, space: 'space1' },
|
||||
alerts,
|
||||
getAlerts,
|
||||
}: {
|
||||
supertest: SuperTest.SuperTest<SuperTest.Test>;
|
||||
totalCases: number;
|
||||
indicesOfCaseToDelete: number[];
|
||||
owner: string;
|
||||
expectedHttpCode?: number;
|
||||
deleteCaseAuth?: { user: User; space: string | null };
|
||||
alerts: Alerts;
|
||||
getAlerts: (alerts: Alerts) => Promise<Array<Record<string, unknown>>>;
|
||||
}) => {
|
||||
const updatedCases = await createCaseAndAttachAlert({
|
||||
supertest,
|
||||
totalCases,
|
||||
owner,
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
|
||||
const casesToDelete = updatedCases.filter((_, filterIndex) =>
|
||||
indicesOfCaseToDelete.some((indexToDelete) => indexToDelete === filterIndex)
|
||||
);
|
||||
|
||||
const caseIdsToDelete = casesToDelete.map((theCase) => theCase.id);
|
||||
|
||||
await deleteCases({
|
||||
supertest,
|
||||
caseIDs: caseIdsToDelete,
|
||||
expectedHttpCode,
|
||||
auth: deleteCaseAuth,
|
||||
});
|
||||
|
||||
const alertAfterDeletion = await getAlerts(alerts);
|
||||
const caseIdsWithoutRemovedCase = getCaseIdsWithoutRemovedCases({
|
||||
expectedHttpCode,
|
||||
updatedCases,
|
||||
caseIdsToDelete,
|
||||
});
|
||||
|
||||
for (const alert of alertAfterDeletion) {
|
||||
expect(alert[ALERT_CASE_IDS]).eql(caseIdsWithoutRemovedCase);
|
||||
}
|
||||
};
|
||||
|
||||
export const createCaseAndAttachAlert = async ({
|
||||
supertest,
|
||||
totalCases,
|
||||
owner,
|
||||
alerts,
|
||||
getAlerts,
|
||||
}: {
|
||||
supertest: SuperTest.SuperTest<SuperTest.Test>;
|
||||
totalCases: number;
|
||||
owner: string;
|
||||
alerts: Alerts;
|
||||
getAlerts: (alerts: Alerts) => Promise<Array<Record<string, unknown>>>;
|
||||
}): Promise<CaseResponse[]> => {
|
||||
const cases = await Promise.all(
|
||||
[...Array(totalCases).keys()].map((index) =>
|
||||
createCase(
|
||||
|
@ -147,25 +241,21 @@ export const createCaseAttachAlertAndDeleteAlert = async ({
|
|||
expect(alert[ALERT_CASE_IDS]).eql(caseIds);
|
||||
}
|
||||
|
||||
const caseToDelete = updatedCases[indexOfCaseToDelete];
|
||||
|
||||
await deleteAllComments({
|
||||
supertest,
|
||||
caseId: caseToDelete.id,
|
||||
expectedHttpCode,
|
||||
auth: deleteCommentAuth,
|
||||
});
|
||||
|
||||
const alertAfterDeletion = await getAlerts(alerts);
|
||||
|
||||
const caseIdsWithoutRemovedCase =
|
||||
expectedHttpCode === 204
|
||||
? updatedCases
|
||||
.filter((theCase) => theCase.id !== caseToDelete.id)
|
||||
.map((theCase) => theCase.id)
|
||||
: updatedCases.map((theCase) => theCase.id);
|
||||
|
||||
for (const alert of alertAfterDeletion) {
|
||||
expect(alert[ALERT_CASE_IDS]).eql(caseIdsWithoutRemovedCase);
|
||||
}
|
||||
return updatedCases;
|
||||
};
|
||||
|
||||
export const getCaseIdsWithoutRemovedCases = ({
|
||||
updatedCases,
|
||||
caseIdsToDelete,
|
||||
expectedHttpCode,
|
||||
}: {
|
||||
expectedHttpCode: number;
|
||||
updatedCases: Array<{ id: string }>;
|
||||
caseIdsToDelete: string[];
|
||||
}) => {
|
||||
return expectedHttpCode === 204
|
||||
? updatedCases
|
||||
.filter((theCase) => !caseIdsToDelete.some((id) => theCase.id === id))
|
||||
.map((theCase) => theCase.id)
|
||||
: updatedCases.map((theCase) => theCase.id);
|
||||
};
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
import expect from '@kbn/expect';
|
||||
import type SuperTest from 'supertest';
|
||||
import { MAX_DOCS_PER_PAGE } from '@kbn/cases-plugin/common/constants';
|
||||
import {
|
||||
Alerts,
|
||||
createCaseAttachAlertAndDeleteCase,
|
||||
createSecuritySolutionAlerts,
|
||||
getAlertById,
|
||||
getSecuritySolutionAlerts,
|
||||
} from '../../../../common/lib/alerts';
|
||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
|
||||
import {
|
||||
|
@ -39,6 +46,10 @@ import {
|
|||
noKibanaPrivileges,
|
||||
obsOnly,
|
||||
superUser,
|
||||
obsOnlyReadAlerts,
|
||||
obsSec,
|
||||
secSolutionOnlyReadNoIndexAlerts,
|
||||
secOnlyReadAlerts,
|
||||
} from '../../../../common/lib/authentication/users';
|
||||
import {
|
||||
secAllUser,
|
||||
|
@ -51,12 +62,19 @@ import {
|
|||
SECURITY_SOLUTION_FILE_KIND,
|
||||
} from '../../../../common/lib/constants';
|
||||
import { User } from '../../../../common/lib/authentication/types';
|
||||
import {
|
||||
createSignalsIndex,
|
||||
deleteAllRules,
|
||||
deleteSignalsIndex,
|
||||
} from '../../../../../detection_engine_api_integration/utils';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const supertest = getService('supertest');
|
||||
const es = getService('es');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
|
||||
describe('delete_cases', () => {
|
||||
afterEach(async () => {
|
||||
|
@ -209,6 +227,197 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('alerts', () => {
|
||||
describe('security_solution', () => {
|
||||
let alerts: Alerts = [];
|
||||
|
||||
const getAlerts = async (_alerts: Alerts) => {
|
||||
await es.indices.refresh({ index: _alerts.map((alert) => alert._index) });
|
||||
const updatedAlerts = await getSecuritySolutionAlerts(
|
||||
supertest,
|
||||
alerts.map((alert) => alert._id)
|
||||
);
|
||||
|
||||
return updatedAlerts.hits.hits.map((alert) => ({ ...alert._source }));
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await createSignalsIndex(supertest, log);
|
||||
const signals = await createSecuritySolutionAlerts(supertest, log);
|
||||
alerts = [signals.hits.hits[0], signals.hits.hits[1]];
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await deleteSignalsIndex(supertest, log);
|
||||
await deleteAllRules(supertest, log);
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
});
|
||||
|
||||
it('removes a case from the alert schema when deleting a case', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indicesOfCaseToDelete: [0],
|
||||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
});
|
||||
|
||||
it('removes multiple cases from the alert schema when deleting all cases', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 2,
|
||||
indicesOfCaseToDelete: [0, 1],
|
||||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
});
|
||||
|
||||
it('removes multiple cases from the alert schema when deleting multiple cases', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 4,
|
||||
indicesOfCaseToDelete: [0, 2],
|
||||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete case ID from the alert schema when the user has read access only', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indicesOfCaseToDelete: [0],
|
||||
expectedHttpCode: 204,
|
||||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
deleteCaseAuth: { user: secOnlyReadAlerts, space: 'space1' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indicesOfCaseToDelete: [0],
|
||||
expectedHttpCode: 204,
|
||||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
deleteCaseAuth: { user: obsSec, space: 'space1' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete the case ID from the alert schema when the user has read access to the kibana feature but no read access to the ES index', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indicesOfCaseToDelete: [0],
|
||||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
expectedHttpCode: 204,
|
||||
deleteCaseAuth: { user: secSolutionOnlyReadNoIndexAlerts, space: 'space1' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('observability', () => {
|
||||
const alerts = [
|
||||
{ _id: 'NoxgpHkBqbdrfX07MqXV', _index: '.alerts-observability.apm.alerts' },
|
||||
{ _id: 'space1alert', _index: '.alerts-observability.apm.alerts' },
|
||||
];
|
||||
|
||||
const getAlerts = async (_alerts: Alerts) => {
|
||||
await es.indices.refresh({ index: '.alerts-observability.apm.alerts' });
|
||||
const updatedAlerts = await Promise.all(
|
||||
_alerts.map((alert) =>
|
||||
getAlertById({
|
||||
supertest: supertestWithoutAuth,
|
||||
id: alert._id,
|
||||
index: alert._index,
|
||||
auth: { user: superUser, space: 'space1' },
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
return updatedAlerts as Array<Record<string, unknown>>;
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/rule_registry/alerts');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/rule_registry/alerts');
|
||||
});
|
||||
|
||||
it('removes a case from the alert schema when deleting a case', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indicesOfCaseToDelete: [0],
|
||||
owner: 'observabilityFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
});
|
||||
|
||||
it('removes multiple cases from the alert schema when deleting all cases', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 2,
|
||||
indicesOfCaseToDelete: [0, 1],
|
||||
owner: 'observabilityFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
});
|
||||
|
||||
it('removes multiple cases from the alert schema when deleting multiple cases', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 4,
|
||||
indicesOfCaseToDelete: [0, 2],
|
||||
owner: 'observabilityFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete case ID from the alert schema when the user has read access only', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indicesOfCaseToDelete: [0],
|
||||
expectedHttpCode: 204,
|
||||
owner: 'observabilityFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
deleteCaseAuth: { user: obsOnlyReadAlerts, space: 'space1' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
await createCaseAttachAlertAndDeleteCase({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indicesOfCaseToDelete: [0],
|
||||
expectedHttpCode: 204,
|
||||
owner: 'observabilityFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
deleteCaseAuth: { user: obsSec, space: 'space1' },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rbac', () => {
|
||||
describe('files', () => {
|
||||
// we need api_int_users and roles because they have authorization for the actual plugins (not the fixtures). This
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import {
|
||||
Alerts,
|
||||
createCaseAttachAlertAndDeleteAlert,
|
||||
createSecuritySolutionAlerts,
|
||||
getAlertById,
|
||||
|
@ -109,8 +110,6 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
|
||||
describe('alerts', () => {
|
||||
type Alerts = Array<{ _id: string; _index: string }>;
|
||||
|
||||
describe('security_solution', () => {
|
||||
let alerts: Alerts = [];
|
||||
|
||||
|
@ -172,7 +171,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should NOT delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
it('should delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
await createCaseAttachAlertAndDeleteAlert({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
|
@ -180,7 +179,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
expectedHttpCode: 403,
|
||||
expectedHttpCode: 204,
|
||||
deleteCommentAuth: { user: obsSec, space: 'space1' },
|
||||
});
|
||||
});
|
||||
|
@ -206,6 +205,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
];
|
||||
|
||||
const getAlerts = async (_alerts: Alerts) => {
|
||||
await es.indices.refresh({ index: '.alerts-observability.apm.alerts' });
|
||||
const updatedAlerts = await Promise.all(
|
||||
_alerts.map((alert) =>
|
||||
getAlertById({
|
||||
|
@ -263,12 +263,12 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should NOT delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
it('should delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
await createCaseAttachAlertAndDeleteAlert({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indexOfCaseToDelete: 0,
|
||||
expectedHttpCode: 403,
|
||||
expectedHttpCode: 204,
|
||||
owner: 'observabilityFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
|
|
|
@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should NOT delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
it('should delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
await createCaseAttachAlertAndDeleteAlert({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
|
@ -222,7 +222,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
owner: 'securitySolutionFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
expectedHttpCode: 403,
|
||||
expectedHttpCode: 204,
|
||||
deleteCommentAuth: { user: obsSec, space: 'space1' },
|
||||
});
|
||||
});
|
||||
|
@ -248,6 +248,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
];
|
||||
|
||||
const getAlerts = async (_alerts: Alerts) => {
|
||||
await es.indices.refresh({ index: '.alerts-observability.apm.alerts' });
|
||||
const updatedAlerts = await Promise.all(
|
||||
_alerts.map((alert) =>
|
||||
getAlertById({
|
||||
|
@ -346,12 +347,12 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should NOT delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
it('should delete case ID from the alert schema when the user does NOT have access to the alert', async () => {
|
||||
await createCaseAttachAlertAndDeleteAlert({
|
||||
supertest: supertestWithoutAuth,
|
||||
totalCases: 1,
|
||||
indexOfCaseToDelete: 0,
|
||||
expectedHttpCode: 403,
|
||||
expectedHttpCode: 204,
|
||||
owner: 'observabilityFixture',
|
||||
alerts,
|
||||
getAlerts,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue