Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ readonly links: {
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
Expand Down

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/core/public/doc_links/doc_links_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export class DocLinksService {
siem: {
guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`,
privileges: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/sec-requirements.html`,
ml: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/machine-learning.html`,
ruleChangeLog: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/prebuilt-rules-changelog.html`,
detectionsReq: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/detections-permissions-section.html`,
Expand Down Expand Up @@ -570,6 +571,7 @@ export interface DocLinksStart {
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
Expand Down
1 change: 1 addition & 0 deletions src/core/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ export interface DocLinksStart {
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/cases/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"id":"cases",
"kibanaVersion":"kibana",
"optionalPlugins":[
"ruleRegistry",
"security",
"spaces"
],
Expand Down
14 changes: 11 additions & 3 deletions x-pack/plugins/cases/server/client/alerts/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ export const get = async (
{ alertsInfo }: AlertGet,
clientArgs: CasesClientArgs
): Promise<CasesClientGetAlertsResponse> => {
const { alertsService, logger } = clientArgs;
const { alertsService, scopedClusterClient, logger } = clientArgs;
if (alertsInfo.length === 0) {
return [];
}

const alerts = await alertsService.getAlerts({ alertsInfo, logger });
return alerts ?? [];
const alerts = await alertsService.getAlerts({ alertsInfo, scopedClusterClient, logger });
if (!alerts) {
return [];
}

return alerts.docs.map((alert) => ({
id: alert._id,
index: alert._index,
...alert._source,
}));
};
12 changes: 11 additions & 1 deletion x-pack/plugins/cases/server/client/alerts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@

import { CaseStatuses } from '../../../common/api';
import { AlertInfo } from '../../common';
import { Alert } from '../../services/alerts/types';

interface Alert {
id: string;
index: string;
destination?: {
ip: string;
};
source?: {
ip: string;
};
}

export type CasesClientGetAlertsResponse = Alert[];

Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/cases/server/client/alerts/update_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export const updateStatus = async (
{ alerts }: UpdateAlertsStatusArgs,
clientArgs: CasesClientArgs
): Promise<void> => {
const { alertsService, logger } = clientArgs;
await alertsService.updateAlertsStatus({ alerts, logger });
const { alertsService, scopedClusterClient, logger } = clientArgs;
await alertsService.updateAlertsStatus({ alerts, scopedClusterClient, logger });
};
34 changes: 31 additions & 3 deletions x-pack/plugins/cases/server/client/attachments/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ import {
} from '../../services/user_actions/helpers';

import { AttachmentService, CasesService, CaseUserActionService } from '../../services';
import { createCaseError, CommentableCase, isCommentRequestTypeGenAlert } from '../../common';
import {
createCaseError,
CommentableCase,
createAlertUpdateRequest,
isCommentRequestTypeGenAlert,
} from '../../common';
import { CasesClientArgs, CasesClientInternal } from '..';

import { decodeCommentRequest } from '../utils';
Expand Down Expand Up @@ -190,9 +195,22 @@ const addGeneratedAlerts = async (
user: userDetails,
commentReq: query,
id: savedObjectID,
casesClientInternal,
});

if (
(newComment.attributes.type === CommentType.alert ||
newComment.attributes.type === CommentType.generatedAlert) &&
caseInfo.attributes.settings.syncAlerts
) {
const alertsToUpdate = createAlertUpdateRequest({
comment: query,
status: subCase.attributes.status,
});
await casesClientInternal.alerts.updateStatus({
alerts: alertsToUpdate,
});
}

await userActionService.bulkCreate({
unsecuredSavedObjectsClient,
actions: [
Expand Down Expand Up @@ -368,9 +386,19 @@ export const addComment = async (
user: userInfo,
commentReq: query,
id: savedObjectID,
casesClientInternal,
});

if (newComment.attributes.type === CommentType.alert && updatedCase.settings.syncAlerts) {
const alertsToUpdate = createAlertUpdateRequest({
comment: query,
status: updatedCase.status,
});

await casesClientInternal.alerts.updateStatus({
alerts: alertsToUpdate,
});
}

await userActionService.bulkCreate({
unsecuredSavedObjectsClient,
actions: [
Expand Down
65 changes: 21 additions & 44 deletions x-pack/plugins/cases/server/client/cases/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import Boom from '@hapi/boom';
import { SavedObjectsFindResponse, SavedObject, Logger } from 'kibana/server';
import { SavedObjectsFindResponse, SavedObject } from 'kibana/server';

import {
ActionConnector,
Expand All @@ -22,16 +22,26 @@ import {
import { buildCaseUserActionItem } from '../../services/user_actions/helpers';

import { createIncident, getCommentContextFromAttributes } from './utils';
import {
AlertInfo,
createCaseError,
flattenCaseSavedObject,
getAlertInfoFromComments,
} from '../../common';
import { createCaseError, flattenCaseSavedObject, getAlertInfoFromComments } from '../../common';
import { CasesClient, CasesClientArgs, CasesClientInternal } from '..';
import { Operations } from '../../authorization';
import { casesConnectors } from '../../connectors';
import { CasesClientGetAlertsResponse } from '../alerts/types';

/**
* Returns true if the case should be closed based on the configuration settings and whether the case
* is a collection. Collections are not closable because we aren't allowing their status to be changed.
* In the future we could allow push to close all the sub cases of a collection but that's not currently supported.
*/
function shouldCloseByPush(
configureSettings: SavedObjectsFindResponse<CasesConfigureAttributes>,
caseInfo: SavedObject<CaseAttributes>
): boolean {
return (
configureSettings.total > 0 &&
configureSettings.saved_objects[0].attributes.closure_type === 'close-by-pushing' &&
caseInfo.attributes.type !== CaseType.collection
);
}

/**
* Parameters for pushing a case to an external system
Expand Down Expand Up @@ -96,7 +106,9 @@ export const push = async (

const alertsInfo = getAlertInfoFromComments(theCase?.comments);

const alerts = await getAlertsCatchErrors({ casesClientInternal, alertsInfo, logger });
const alerts = await casesClientInternal.alerts.get({
alertsInfo,
});

const getMappingsResponse = await casesClientInternal.configuration.getMappings({
connector: theCase.connector,
Expand Down Expand Up @@ -266,38 +278,3 @@ export const push = async (
throw createCaseError({ message: `Failed to push case: ${error}`, error, logger });
}
};

async function getAlertsCatchErrors({
casesClientInternal,
alertsInfo,
logger,
}: {
casesClientInternal: CasesClientInternal;
alertsInfo: AlertInfo[];
logger: Logger;
}): Promise<CasesClientGetAlertsResponse> {
try {
return await casesClientInternal.alerts.get({
alertsInfo,
});
} catch (error) {
logger.error(`Failed to retrieve alerts during push: ${error}`);
return [];
}
}

/**
* Returns true if the case should be closed based on the configuration settings and whether the case
* is a collection. Collections are not closable because we aren't allowing their status to be changed.
* In the future we could allow push to close all the sub cases of a collection but that's not currently supported.
*/
function shouldCloseByPush(
configureSettings: SavedObjectsFindResponse<CasesConfigureAttributes>,
caseInfo: SavedObject<CaseAttributes>
): boolean {
return (
configureSettings.total > 0 &&
configureSettings.saved_objects[0].attributes.closure_type === 'close-by-pushing' &&
caseInfo.attributes.type !== CaseType.collection
);
}
27 changes: 10 additions & 17 deletions x-pack/plugins/cases/server/client/cases/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';

import {
Logger,
SavedObject,
SavedObjectsClientContract,
SavedObjectsFindResponse,
Expand Down Expand Up @@ -308,14 +307,12 @@ async function updateAlerts({
caseService,
unsecuredSavedObjectsClient,
casesClientInternal,
logger,
}: {
casesWithSyncSettingChangedToOn: UpdateRequestWithOriginalCase[];
casesWithStatusChangedAndSynced: UpdateRequestWithOriginalCase[];
caseService: CasesService;
unsecuredSavedObjectsClient: SavedObjectsClientContract;
casesClientInternal: CasesClientInternal;
logger: Logger;
}) {
/**
* It's possible that a case ID can appear multiple times in each array. I'm intentionally placing the status changes
Expand Down Expand Up @@ -364,9 +361,7 @@ async function updateAlerts({
[]
);

await casesClientInternal.alerts.updateStatus({
alerts: alertsToUpdate,
});
await casesClientInternal.alerts.updateStatus({ alerts: alertsToUpdate });
}

function partitionPatchRequest(
Expand Down Expand Up @@ -567,6 +562,15 @@ export const update = async (
);
});

// Update the alert's status to match any case status or sync settings changes
await updateAlerts({
casesWithStatusChangedAndSynced,
casesWithSyncSettingChangedToOn,
caseService,
unsecuredSavedObjectsClient,
casesClientInternal,
});

const returnUpdatedCase = myCases.saved_objects
.filter((myCase) =>
updatedCases.saved_objects.some((updatedCase) => updatedCase.id === myCase.id)
Expand Down Expand Up @@ -594,17 +598,6 @@ export const update = async (
}),
});

// Update the alert's status to match any case status or sync settings changes
// Attempt to do this after creating/changing the other entities just in case it fails
await updateAlerts({
casesWithStatusChangedAndSynced,
casesWithSyncSettingChangedToOn,
caseService,
unsecuredSavedObjectsClient,
casesClientInternal,
logger,
});

return CasesResponseRt.encode(returnUpdatedCase);
} catch (error) {
const idVersions = cases.cases.map((caseInfo) => ({
Expand Down
19 changes: 11 additions & 8 deletions x-pack/plugins/cases/server/client/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
* 2.0.
*/

import { KibanaRequest, SavedObjectsServiceStart, Logger } from 'kibana/server';
import {
KibanaRequest,
SavedObjectsServiceStart,
Logger,
ElasticsearchClient,
} from 'kibana/server';
import { SecurityPluginSetup, SecurityPluginStart } from '../../../security/server';
import { SAVED_OBJECT_TYPES } from '../../common';
import { Authorization } from '../authorization/authorization';
Expand All @@ -20,8 +25,8 @@ import {
} from '../services';
import { PluginStartContract as FeaturesPluginStart } from '../../../features/server';
import { PluginStartContract as ActionsPluginStart } from '../../../actions/server';
import { RuleRegistryPluginStartContract } from '../../../rule_registry/server';
import { LensServerPluginSetup } from '../../../lens/server';

import { AuthorizationAuditLogger } from '../authorization';
import { CasesClient, createCasesClient } from '.';

Expand All @@ -31,7 +36,6 @@ interface CasesClientFactoryArgs {
getSpace: GetSpaceFn;
featuresPluginStart: FeaturesPluginStart;
actionsPluginStart: ActionsPluginStart;
ruleRegistryPluginStart?: RuleRegistryPluginStartContract;
lensEmbeddableFactory: LensServerPluginSetup['lensEmbeddableFactory'];
}

Expand Down Expand Up @@ -65,10 +69,12 @@ export class CasesClientFactory {
*/
public async create({
request,
scopedClusterClient,
savedObjectsService,
}: {
request: KibanaRequest;
savedObjectsService: SavedObjectsServiceStart;
scopedClusterClient: ElasticsearchClient;
}): Promise<CasesClient> {
if (!this.isInitialized || !this.options) {
throw new Error('CasesClientFactory must be initialized before calling create');
Expand All @@ -88,12 +94,9 @@ export class CasesClientFactory {
const caseService = new CasesService(this.logger, this.options?.securityPluginStart?.authc);
const userInfo = caseService.getUser({ request });

const alertsClient = await this.options.ruleRegistryPluginStart?.getRacClientWithRequest(
request
);

return createCasesClient({
alertsService: new AlertService(alertsClient),
alertsService: new AlertService(),
scopedClusterClient,
unsecuredSavedObjectsClient: savedObjectsService.getScopedClient(request, {
includedHiddenTypes: SAVED_OBJECT_TYPES,
// this tells the security plugin to not perform SO authorization and audit logging since we are handling
Expand Down
Loading