diff --git a/x-pack/plugins/security_solution/server/lib/note/saved_object.ts b/x-pack/plugins/security_solution/server/lib/note/saved_object.ts index bf6090f0337f7..7c5bb0ebf7b44 100644 --- a/x-pack/plugins/security_solution/server/lib/note/saved_object.ts +++ b/x-pack/plugins/security_solution/server/lib/note/saved_object.ts @@ -136,7 +136,8 @@ export const persistNote = async ( request: FrameworkRequest, noteId: string | null, version: string | null, - note: SavedNote + note: SavedNote, + overideNote: boolean = true ): Promise => { try { const savedObjectsClient = request.context.core.savedObjects.client; @@ -163,14 +164,14 @@ export const persistNote = async ( note: convertSavedObjectToSavedNote( await savedObjectsClient.create( noteSavedObjectType, - pickSavedNote(noteId, note, request.user) + overideNote ? pickSavedNote(noteId, note, request.user) : note ), timelineVersionSavedObject != null ? timelineVersionSavedObject : undefined ), }; } - // Update new note + // Update existing note const existingNote = await getSavedNote(request, noteId); return { @@ -180,7 +181,7 @@ export const persistNote = async ( await savedObjectsClient.update( noteSavedObjectType, noteId, - pickSavedNote(noteId, note, request.user), + overideNote ? pickSavedNote(noteId, note, request.user) : note, { version: existingNote.version || undefined, } diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts index b817896e901c1..1e12382fafb6b 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.test.ts @@ -243,16 +243,28 @@ describe('import timelines', () => { expect(mockPersistNote.mock.calls[0][3]).toEqual({ eventId: undefined, note: mockUniqueParsedObjects[0].globalNotes[0].note, + created: mockUniqueParsedObjects[0].globalNotes[0].created, + createdBy: mockUniqueParsedObjects[0].globalNotes[0].createdBy, + updated: mockUniqueParsedObjects[0].globalNotes[0].updated, + updatedBy: mockUniqueParsedObjects[0].globalNotes[0].updatedBy, timelineId: mockCreatedTimeline.savedObjectId, }); expect(mockPersistNote.mock.calls[1][3]).toEqual({ eventId: mockUniqueParsedObjects[0].eventNotes[0].eventId, note: mockUniqueParsedObjects[0].eventNotes[0].note, + created: mockUniqueParsedObjects[0].eventNotes[0].created, + createdBy: mockUniqueParsedObjects[0].eventNotes[0].createdBy, + updated: mockUniqueParsedObjects[0].eventNotes[0].updated, + updatedBy: mockUniqueParsedObjects[0].eventNotes[0].updatedBy, timelineId: mockCreatedTimeline.savedObjectId, }); expect(mockPersistNote.mock.calls[2][3]).toEqual({ eventId: mockUniqueParsedObjects[0].eventNotes[1].eventId, note: mockUniqueParsedObjects[0].eventNotes[1].note, + created: mockUniqueParsedObjects[0].eventNotes[1].created, + createdBy: mockUniqueParsedObjects[0].eventNotes[1].createdBy, + updated: mockUniqueParsedObjects[0].eventNotes[1].updated, + updatedBy: mockUniqueParsedObjects[0].eventNotes[1].updatedBy, timelineId: mockCreatedTimeline.savedObjectId, }); }); @@ -573,6 +585,10 @@ describe('import timeline templates', () => { expect(mockPersistNote.mock.calls[0][3]).toEqual({ eventId: undefined, note: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].note, + created: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].created, + createdBy: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].createdBy, + updated: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].updated, + updatedBy: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].updatedBy, timelineId: mockCreatedTemplateTimeline.savedObjectId, }); }); @@ -721,6 +737,10 @@ describe('import timeline templates', () => { expect(mockPersistNote.mock.calls[0][3]).toEqual({ eventId: undefined, note: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].note, + created: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].created, + createdBy: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].createdBy, + updated: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].updated, + updatedBy: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].updatedBy, timelineId: mockCreatedTemplateTimeline.savedObjectId, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/create_timelines.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/create_timelines.ts index cdedffbbd9458..c764cfe89b520 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/create_timelines.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/create_timelines.ts @@ -45,26 +45,44 @@ export const savePinnedEvents = ( ) ); -export const saveNotes = ( +export const saveNotes = async ( frameworkRequest: FrameworkRequest, timelineSavedObjectId: string, timelineVersion?: string | null, existingNoteIds?: string[], - newNotes?: NoteResult[] + newNotes?: NoteResult[], + overideNotes: boolean = true ) => { return Promise.all( - newNotes?.map((note) => { - const newNote: SavedNote = { - eventId: note.eventId, - note: note.note, - timelineId: timelineSavedObjectId, - }; + newNotes?.map(async (note) => { + let savedNote = note; + try { + savedNote = await noteLib.getNote(frameworkRequest, note.noteId); + // eslint-disable-next-line no-empty + } catch (e) {} + + const newNote: SavedNote = overideNotes + ? { + eventId: note.eventId, + note: note.note, + timelineId: timelineSavedObjectId, + } + : { + eventId: savedNote.eventId, + note: savedNote.note, + created: savedNote.created, + createdBy: savedNote.createdBy, + updated: savedNote.updated, + updatedBy: savedNote.updatedBy, + timelineId: timelineSavedObjectId, + }; return noteLib.persistNote( frameworkRequest, - existingNoteIds?.find((nId) => nId === note.noteId) ?? null, + overideNotes ? existingNoteIds?.find((nId) => nId === note.noteId) ?? null : null, timelineVersion ?? null, - newNote + newNote, + overideNotes ); }) ?? [] ); @@ -75,12 +93,18 @@ interface CreateTimelineProps { timeline: SavedTimeline; timelineSavedObjectId?: string | null; timelineVersion?: string | null; + overideNotes?: boolean; pinnedEventIds?: string[] | null; notes?: NoteResult[]; existingNoteIds?: string[]; isImmutable?: boolean; } +/** allow override means overriding by current username, + * disallow override means keep the original username. + * override = flase only happens when import timeline templates, + * as we want to keep the original creator for notes + **/ export const createTimelines = async ({ frameworkRequest, timeline, @@ -90,6 +114,7 @@ export const createTimelines = async ({ notes = [], existingNoteIds = [], isImmutable, + overideNotes = true, }: CreateTimelineProps): Promise => { const responseTimeline = await saveTimelines( frameworkRequest, @@ -119,7 +144,8 @@ export const createTimelines = async ({ timelineSavedObjectId ?? newTimelineSavedObjectId, newTimelineVersion, existingNoteIds, - notes + notes, + overideNotes ), ]; } diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/import_timelines.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/import_timelines.ts index 996dc5823691d..362f12a7af8ee 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/import_timelines.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/import_timelines.ts @@ -163,6 +163,7 @@ export const importTimelines = async ( pinnedEventIds: isTemplateTimeline ? null : pinnedEventIds, notes: isTemplateTimeline ? globalNotes : [...globalNotes, ...eventNotes], isImmutable, + overideNotes: false, }); resolve({ @@ -196,6 +197,7 @@ export const importTimelines = async ( notes: globalNotes, existingNoteIds: compareTimelinesStatus.timelineInput.data?.noteIds, isImmutable, + overideNotes: false, }); resolve({