diff --git a/x-pack/plugins/cases/common/api/cases/user_actions.ts b/x-pack/plugins/cases/common/api/cases/user_actions.ts index 55dfac391f3be..1b53adb002436 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions.ts @@ -22,6 +22,7 @@ const UserActionFieldTypeRt = rt.union([ rt.literal('status'), rt.literal('settings'), rt.literal('sub_case'), + rt.literal('owner'), ]); const UserActionFieldRt = rt.array(UserActionFieldTypeRt); const UserActionRt = rt.union([ diff --git a/x-pack/plugins/cases/server/authorization/audit_logger.ts b/x-pack/plugins/cases/server/authorization/audit_logger.ts index 2a739ea6e8106..216cf7d9c20e0 100644 --- a/x-pack/plugins/cases/server/authorization/audit_logger.ts +++ b/x-pack/plugins/cases/server/authorization/audit_logger.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { OperationDetails } from '.'; -import { AuditLogger, EventCategory, EventOutcome } from '../../../security/server'; +import { DATABASE_CATEGORY, ECS_OUTCOMES, OperationDetails } from '.'; +import { AuditLogger } from '../../../security/server'; enum AuthorizationResult { Unauthorized = 'Unauthorized', @@ -51,9 +51,9 @@ export class AuthorizationAuditLogger { message: `${username ?? 'unknown user'} ${message}`, event: { action: operation.action, - category: EventCategory.DATABASE, - type: operation.type, - outcome: EventOutcome.SUCCESS, + category: DATABASE_CATEGORY, + type: [operation.type], + outcome: ECS_OUTCOMES.success, }, ...(username != null && { user: { @@ -81,9 +81,9 @@ export class AuthorizationAuditLogger { message: `${username ?? 'unknown user'} ${message}`, event: { action: operation.action, - category: EventCategory.DATABASE, - type: operation.type, - outcome: EventOutcome.FAILURE, + category: DATABASE_CATEGORY, + type: [operation.type], + outcome: ECS_OUTCOMES.failure, }, // add the user information if we have it ...(username != null && { diff --git a/x-pack/plugins/cases/server/authorization/index.ts b/x-pack/plugins/cases/server/authorization/index.ts index 01e03ceb9b5aa..994f0cf6adb6b 100644 --- a/x-pack/plugins/cases/server/authorization/index.ts +++ b/x-pack/plugins/cases/server/authorization/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { EventType } from '../../../security/server'; +import { EcsEventCategory, EcsEventOutcome, EcsEventType } from 'kibana/server'; import { CASE_COMMENT_SAVED_OBJECT, CASE_CONFIGURE_SAVED_OBJECT, @@ -41,13 +41,34 @@ const deleteVerbs: Verbs = { past: 'deleted', }; +const eventTypes: Record = { + creation: 'creation', + deletion: 'deletion', + change: 'change', + access: 'access', +}; + +/** + * Database constant for ECS category for use for audit logging. + */ +export const DATABASE_CATEGORY: EcsEventCategory[] = ['database']; + +/** + * ECS Outcomes for audit logging. + */ +export const ECS_OUTCOMES: Record = { + failure: 'failure', + success: 'success', + unknown: 'unknown', +}; + /** * Definition of all APIs within the cases backend. */ export const Operations: Record = { // case operations [WriteOperations.CreateCase]: { - type: EventType.CREATION, + type: eventTypes.creation, name: WriteOperations.CreateCase, action: 'create-case', verbs: createVerbs, @@ -55,7 +76,7 @@ export const Operations: Record Promise; -// TODO: we need to have an operation per entity route so I think we need to create a bunch like -// getCase, getComment, getSubCase etc for each, need to think of a clever way of creating them for all the routes easily? - -// if you add a value here you'll likely also need to make changes here: -// x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts +/** + * Read operations for the cases APIs. + * + * NOTE: If you add a value here you'll likely also need to make changes here: + * x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts + */ export enum ReadOperations { GetCase = 'getCase', FindCases = 'findCases', @@ -37,7 +37,12 @@ export enum ReadOperations { FindConfigurations = 'findConfigurations', } -// TODO: comments +/** + * Write operations for the cases APIs. + * + * NOTE: If you add a value here you'll likely also need to make changes here: + * x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/cases.ts + */ export enum WriteOperations { CreateCase = 'createCase', DeleteCase = 'deleteCase', @@ -54,7 +59,7 @@ export enum WriteOperations { * Defines the structure for a case API route. */ export interface OperationDetails { - type: EventType; + type: EcsEventType; name: ReadOperations | WriteOperations; action: string; verbs: Verbs; diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index 15fbd34628182..3b792ea2ff2aa 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -25,8 +25,7 @@ import { buildCaseUserActionItem } from '../../services/user_actions/helpers'; import { createAuditMsg, ensureAuthorized, getConnectorFromConfiguration } from '../utils'; import { createCaseError } from '../../common/error'; -import { Operations } from '../../authorization'; -import { EventOutcome } from '../../../../security/server'; +import { ECS_OUTCOMES, Operations } from '../../authorization'; import { ENABLE_CASE_CONNECTOR } from '../../../common/constants'; import { flattenCaseSavedObject, @@ -86,7 +85,7 @@ export const create = async ( auditLogger?.log( createAuditMsg({ operation: Operations.createCase, - outcome: EventOutcome.UNKNOWN, + outcome: ECS_OUTCOMES.unknown, savedObjectID, }) ); diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 4657df2e71b30..7fc2b3927c22c 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -12,9 +12,8 @@ import { CasesClientArgs } from '..'; import { createCaseError } from '../../common/error'; import { AttachmentService, CaseService } from '../../services'; import { buildCaseUserActionItem } from '../../services/user_actions/helpers'; -import { Operations } from '../../authorization'; +import { ECS_OUTCOMES, Operations } from '../../authorization'; import { createAuditMsg, ensureAuthorized } from '../utils'; -import { EventOutcome } from '../../../../security/server'; async function deleteSubCases({ attachmentService, @@ -93,7 +92,7 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P auditLogger?.log( createAuditMsg({ operation: Operations.deleteCase, - outcome: EventOutcome.UNKNOWN, + outcome: ECS_OUTCOMES.unknown, savedObjectID, }) ); diff --git a/x-pack/plugins/cases/server/client/configure/client.ts b/x-pack/plugins/cases/server/client/configure/client.ts index 1037a2ff9d893..d810c2682618e 100644 --- a/x-pack/plugins/cases/server/client/configure/client.ts +++ b/x-pack/plugins/cases/server/client/configure/client.ts @@ -32,7 +32,6 @@ import { transformCaseConnectorToEsConnector, transformESConnectorToCaseConnector, } from '../../common'; -import { EventOutcome } from '../../../../security/server'; import { CasesClientInternal } from '../client_internal'; import { CasesClientArgs } from '../types'; import { getFields } from './get_fields'; @@ -41,7 +40,7 @@ import { getMappings } from './get_mappings'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { FindActionResult } from '../../../../actions/server/types'; import { ActionType } from '../../../../actions/common'; -import { Operations } from '../../authorization'; +import { ECS_OUTCOMES, Operations } from '../../authorization'; import { combineAuthorizedAndOwnerFilter, createAuditMsg, @@ -280,7 +279,7 @@ async function update( auditLogger?.log( createAuditMsg({ operation: Operations.updateConfiguration, - outcome: EventOutcome.UNKNOWN, + outcome: ECS_OUTCOMES.unknown, savedObjectID: configuration.id, }) ); @@ -430,7 +429,7 @@ async function create( auditLogger?.log( createAuditMsg({ operation: Operations.createConfiguration, - outcome: EventOutcome.UNKNOWN, + outcome: ECS_OUTCOMES.unknown, savedObjectID, }) ); diff --git a/x-pack/plugins/cases/server/client/utils.ts b/x-pack/plugins/cases/server/client/utils.ts index 6e69f2c6fc406..38166262fcc5e 100644 --- a/x-pack/plugins/cases/server/client/utils.ts +++ b/x-pack/plugins/cases/server/client/utils.ts @@ -12,7 +12,7 @@ import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; -import { SavedObjectsFindResponse } from 'kibana/server'; +import { EcsEventOutcome, SavedObjectsFindResponse } from 'kibana/server'; import { PublicMethodsOf } from '@kbn/utility-types'; import { nodeBuilder, KueryNode } from '../../../../../src/plugins/data/common'; import { esKuery } from '../../../../../src/plugins/data/server'; @@ -29,7 +29,7 @@ import { AlertCommentRequestRt, } from '../../common/api'; import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../common/constants'; -import { AuditEvent, EventCategory, EventOutcome } from '../../../security/server'; +import { AuditEvent } from '../../../security/server'; import { combineFilterWithAuthorizationFilter } from '../authorization/utils'; import { getIDsAndIndicesAsArrays, @@ -37,7 +37,7 @@ import { isCommentRequestTypeUser, SavedObjectFindOptionsKueryNode, } from '../common'; -import { Authorization, OperationDetails } from '../authorization'; +import { Authorization, DATABASE_CATEGORY, ECS_OUTCOMES, OperationDetails } from '../authorization'; import { AuditLogger } from '../../../security/server'; export const decodeCommentRequest = (comment: CommentRequest) => { @@ -573,7 +573,7 @@ export function createAuditMsg({ }: { operation: OperationDetails; savedObjectID?: string; - outcome?: EventOutcome; + outcome?: EcsEventOutcome; error?: Error; }): AuditEvent { const doc = @@ -582,7 +582,7 @@ export function createAuditMsg({ : `a ${operation.docType}`; const message = error ? `Failed attempt to ${operation.verbs.present} ${doc}` - : outcome === EventOutcome.UNKNOWN + : outcome === ECS_OUTCOMES.unknown ? `User is ${operation.verbs.progressive} ${doc}` : `User has ${operation.verbs.past} ${doc}`; @@ -590,9 +590,9 @@ export function createAuditMsg({ message, event: { action: operation.action, - category: EventCategory.DATABASE, - type: operation.type, - outcome: outcome ?? (error ? EventOutcome.FAILURE : EventOutcome.SUCCESS), + category: DATABASE_CATEGORY, + type: [operation.type], + outcome: outcome ?? (error ? ECS_OUTCOMES.failure : ECS_OUTCOMES.success), }, ...(savedObjectID != null && { kibana: { diff --git a/x-pack/plugins/cases/server/services/user_actions/helpers.ts b/x-pack/plugins/cases/server/services/user_actions/helpers.ts index e987bd1685405..2ab3bdb5e1cee 100644 --- a/x-pack/plugins/cases/server/services/user_actions/helpers.ts +++ b/x-pack/plugins/cases/server/services/user_actions/helpers.ts @@ -157,6 +157,7 @@ const userActionFieldsAllowed: UserActionField = [ 'status', 'settings', 'sub_case', + 'owner', ]; interface CaseSubIDs { diff --git a/x-pack/plugins/security_solution/cypress/objects/case.ts b/x-pack/plugins/security_solution/cypress/objects/case.ts index a0135431c6543..278eab29f0a62 100644 --- a/x-pack/plugins/security_solution/cypress/objects/case.ts +++ b/x-pack/plugins/security_solution/cypress/objects/case.ts @@ -13,6 +13,7 @@ export interface TestCase { description: string; timeline: CompleteTimeline; reporter: string; + owner: string; } export interface Connector { @@ -45,6 +46,7 @@ export const case1: TestCase = { description: 'This is the case description', timeline, reporter: 'elastic', + owner: 'securitySolution', }; export const serviceNowConnector: Connector = { diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts index f73b8e47066d2..798cd184d6012 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts @@ -24,6 +24,7 @@ export const createCase = (newCase: TestCase) => settings: { syncAlerts: true, }, + owner: newCase.owner, }, headers: { 'kbn-xsrf': 'cypress-creds' }, });