From de21dd88c95ffe195a7682702b7beec9a56ca418 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 20 May 2020 14:42:19 -0400 Subject: [PATCH 01/18] Update KeywordSearchIngestModule.java Add creation of TSK_METADATA artifact to ingest module. --- .../KeywordSearchIngestModule.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 72a0df796f..9416bca7d4 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.io.CharSource; import java.io.IOException; import java.io.Reader; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -54,6 +56,10 @@ import org.sleuthkit.autopsy.textextractors.TextFileExtractor; import org.sleuthkit.autopsy.textextractors.configs.ImageConfig; import org.sleuthkit.autopsy.textextractors.configs.StringsConfig; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Blackboard; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData.FileKnown; @@ -114,6 +120,30 @@ public final class KeywordSearchIngestModule implements FileIngestModule { "application/x-z", //NON-NLS "application/x-compress"); //NON-NLS + private static final List METADATA_TYPES + = ImmutableList.of( + "Total-Time", //NON-NLS + "Template", //NON-NLS + "Revision-Number", //NON-NLS + "Last-Save-Date", //NON-NLS + "Last-Printed", //NON-NLS + "Last-Author", //NON-NLS + "Edit-Time", //NON-NLS + "Creation-Date", //NON-NLS + "Company", //NON-NLS + "Author", //NON-NLS + "Application-Name", //NON-NLS + "protected", //NON-NLS + "SourceModified", //NON-NLS + "Last-Modified", //NON-NLS + "Producer", //NON-NLS + "pdf:docinfo:creator_tool", //NON-NLS + "Title", //NON-NLS + "pdf:encrypted", //NON-NLS + "Description", //NON-NLS + "pdf:PDFVersion"); //NON-NLS + + /** * Options for this extractor */ @@ -493,6 +523,9 @@ public final class KeywordSearchIngestModule implements FileIngestModule { Reader finalReader; try { Map metadata = extractor.getMetadata(); + if (!metadata.isEmpty()) { + createMetadataArtifact(aFile, metadata); + } CharSource formattedMetadata = getMetaDataCharSource(metadata); //Append the metadata to end of the file text finalReader = CharSource.concat(new CharSource() { @@ -515,6 +548,38 @@ public final class KeywordSearchIngestModule implements FileIngestModule { return false; } } + + private void createMetadataArtifact(AbstractFile aFile, Map metadata) { + + String moduleName = KeywordSearchIngestModule.class.getName(); + Collection attributes = new ArrayList<>(); + Collection bbartifacts = new ArrayList<>(); + for (Map.Entry entry : metadata.entrySet()) { + if (METADATA_TYPES.contains(entry.getKey())) { + if (!entry.getValue().isEmpty() && !entry.getValue().contentEquals(" ")) { + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, entry.getKey())); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE, moduleName, entry.getValue())); + try { + BlackboardArtifact bbart = aFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA); + bbart.addAttributes(attributes); + bbartifacts.add(bbart); + } catch (TskCoreException ex) { + // return and continue processing + return; + } + } + } + } + if (!bbartifacts.isEmpty()) { + try{ + Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().postArtifacts(bbartifacts, moduleName); + } catch (NoCurrentCaseException | Blackboard.BlackboardException ex) { + // Ignore this and continue on + //logger.log(Level.SEVERE, "Unable to post blackboard artifacts", ex); //NON-NLS + return; + } + } + } /** * Pretty print the text extractor metadata. From ebf6b36110680f17b691bf1c410254a1146827ac Mon Sep 17 00:00:00 2001 From: NISHIDA Motoki Date: Sat, 23 May 2020 14:59:26 +0900 Subject: [PATCH 02/18] Correct translation error. - "Modified time" was not translated correctly. --- .../autopsy/datamodel/Bundle_ja.properties | 36 +++++++++---------- .../autopsy/report/Bundle_ja.properties | 8 ++--- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties index ee9b6fb4b6..40abf528ad 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle_ja.properties @@ -1,7 +1,7 @@ -AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u6642\u523b +AbstractAbstractFileNode.accessTimeColLbl=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 AbstractAbstractFileNode.attrAddrColLbl=\u5c5e\u6027\u30a2\u30c9\u30ec\u30b9 -AbstractAbstractFileNode.changeTimeColLbl=\u6642\u523b\u5909\u66f4 -AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u3057\u305f\u6642\u523b +AbstractAbstractFileNode.changeTimeColLbl=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642 +AbstractAbstractFileNode.createdTimeColLbl=\u4f5c\u6210\u65e5\u6642 AbstractAbstractFileNode.createSheet.comment.displayName=C AbstractAbstractFileNode.createSheet.comment.name=C # {0} - occurrenceCount @@ -26,7 +26,7 @@ AbstractAbstractFileNode.md5HashColLbl=MD5\u30cf\u30c3\u30b7\u30e5 AbstractAbstractFileNode.metaAddrColLbl=\u30e1\u30bf\u30a2\u30c9\u30ec\u30b9 AbstractAbstractFileNode.mimeType=MIME\u30bf\u30a4\u30d7 AbstractAbstractFileNode.modeColLbl=\u30e2\u30fc\u30c9 -AbstractAbstractFileNode.modifiedTimeColLbl=MFT\u5909\u66f4\u6642\u523b +AbstractAbstractFileNode.modifiedTimeColLbl=\u66f4\u65b0\u65e5\u6642 AbstractAbstractFileNode.nameColLbl=\u540d\u524d AbstractAbstractFileNode.objectId=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8ID AbstractAbstractFileNode.originalName=\u30aa\u30ea\u30b8\u30ca\u30eb\u540d @@ -136,14 +136,14 @@ ImageNode.createSheet.type.name=\u30bf\u30a4\u30d7 ImageNode.createSheet.type.text=\u30a4\u30e1\u30fc\u30b8 ImageNode.getActions.openFileSearchByAttr.text=\u5c5e\u6027\u5225\u306b\u30d5\u30a1\u30a4\u30eb\u691c\u7d22\u3092\u958b\u304f KeyValueNode.menuItemText.viewFileInDir=\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u5185\u306e\u30bd\u30fc\u30b9\u30d5\u30a1\u30a4\u30eb\u3092\u8868\u793a -KeywordHits.createNodeForKey.accessTime.desc=\u30a2\u30af\u30bb\u30b9\u6642\u523b -KeywordHits.createNodeForKey.accessTime.displayName=\u30a2\u30af\u30bb\u30b9\u6642\u523b +KeywordHits.createNodeForKey.accessTime.desc=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 +KeywordHits.createNodeForKey.accessTime.displayName=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 KeywordHits.createNodeForKey.accessTime.name=AccessTime -KeywordHits.createNodeForKey.chgTime.desc=\u6642\u523b\u5909\u66f4 -KeywordHits.createNodeForKey.chgTime.displayName=\u5909\u66f4\u6642\u523b +KeywordHits.createNodeForKey.chgTime.desc=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642 +KeywordHits.createNodeForKey.chgTime.displayName=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642 KeywordHits.createNodeForKey.chgTime.name=ChangeTime -KeywordHits.createNodeForKey.modTime.desc=MFT\u5909\u66f4\u6642\u523b -KeywordHits.createNodeForKey.modTime.displayName=MFT\u5909\u66f4\u6642\u523b +KeywordHits.createNodeForKey.modTime.desc=\u66f4\u65b0\u65e5\u6642 +KeywordHits.createNodeForKey.modTime.displayName=\u66f4\u65b0\u65e5\u6642 KeywordHits.createNodeForKey.modTime.name=ModifiedTime KeywordHits.createSheet.filesWithHits.desc=\u8aac\u660e\u306a\u3057 KeywordHits.createSheet.filesWithHits.displayName=\u30d2\u30c3\u30c8\u306e\u3042\u308b\u30d5\u30a1\u30a4\u30eb @@ -199,14 +199,14 @@ ContentTagNode.createSheet.filePath.name=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 ContentTagNode.createSheet.filePath.displayName=\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9 ContentTagNode.createSheet.comment.name=\u30b3\u30e1\u30f3\u30c8 ContentTagNode.createSheet.comment.displayName=\u30b3\u30e1\u30f3\u30c8 -ContentTagNode.createSheet.fileModifiedTime.nam=MFT\u5909\u66f4\u6642\u523b -ContentTagNode.createSheet.fileModifiedTime.displayName=MFT\u5909\u66f4\u6642\u523b -ContentTagNode.createSheet.fileChangedTime.name=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b -ContentTagNode.createSheet.fileChangedTime.displayName=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b -ContentTagNode.createSheet.fileAccessedTime.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b -ContentTagNode.createSheet.fileAccessedTime.displayName=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b -ContentTagNode.createSheet.fileCreatedTime.name=\u4f5c\u6210\u3057\u305f\u6642\u523b -ContentTagNode.createSheet.fileCreatedTime.displayName=\u4f5c\u6210\u3057\u305f\u6642\u523b +ContentTagNode.createSheet.fileModifiedTime.nam=\u66f4\u65b0\u65e5\u6642 +ContentTagNode.createSheet.fileModifiedTime.displayName=\u66f4\u65b0\u65e5\u6642 +ContentTagNode.createSheet.fileChangedTime.name=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642 +ContentTagNode.createSheet.fileChangedTime.displayName=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642 +ContentTagNode.createSheet.fileAccessedTime.name=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 +ContentTagNode.createSheet.fileAccessedTime.displayName=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 +ContentTagNode.createSheet.fileCreatedTime.name=\u4f5c\u6210\u65e5\u6642 +ContentTagNode.createSheet.fileCreatedTime.displayName=\u4f5c\u6210\u65e5\u6642 ContentTagNode.createSheet.fileSize.name=\u30b5\u30a4\u30ba ContentTagNode.createSheet.fileSize.displayName=\u30b5\u30a4\u30ba ContentTagTypeNode.displayName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30b0 diff --git a/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties index 6dde31add8..3e109ee0cb 100644 --- a/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/report/Bundle_ja.properties @@ -141,10 +141,10 @@ ReportGenerator.progress.createdThumb.text=\u30b5\u30e0\u30cd\u30a4\u30eb\u306e\ ReportGenerator.htmlOutput.header.file=\u30d5\u30a1\u30a4\u30eb ReportGenerator.htmlOutput.header.tag=\u30bf\u30b0 ReportGenerator.htmlOutput.header.comment=\u30b3\u30e1\u30f3\u30c8 -ReportGenerator.htmlOutput.header.timeModified=MFT\u5909\u66f4\u6642\u523b -ReportGenerator.htmlOutput.header.timeChanged=\u5909\u66f4\u3055\u308c\u305f\u523b\u523b -ReportGenerator.htmlOutput.header.timeAccessed=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u6642\u523b -ReportGenerator.htmlOutput.header.timeCreated=\u4f5c\u6210\u3055\u308c\u305f\u6642\u523b +ReportGenerator.htmlOutput.header.timeModified=\u66f4\u65b0\u65e5\u6642 +ReportGenerator.htmlOutput.header.timeChanged=\u30a8\u30f3\u30c8\u30ea\u66f4\u65b0\u65e5\u6642 +ReportGenerator.htmlOutput.header.timeAccessed=\u30a2\u30af\u30bb\u30b9\u65e5\u6642 +ReportGenerator.htmlOutput.header.timeCreated=\u4f5c\u6210\u65e5\u6642 ReportGenerator.htmlOutput.header.size=\u30b5\u30a4\u30ba(\u30d0\u30a4\u30c8) ReportGenerator.htmlOutput.header.hash=\u30cf\u30c3\u30b7\u30e5 ReportGenerator.thumbnailTable.name=\u30bf\u30b0\u4ed8\u304d\u30a4\u30e1\u30fc\u30b8 From 06e8890417160bd313e34daf48ead1dfb096b824 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 28 May 2020 15:44:09 -0400 Subject: [PATCH 03/18] Added Metadata Artifact to Keyword Search and TImeline Added the Metadata for Documents to TSK_METADATA artifact as well as event types in time line for Metadata. --- .../autopsy/timeline/ui/EventTypeUtils.java | 6 ++ .../KeywordSearchIngestModule.java | 93 +++++++++++-------- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/EventTypeUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/EventTypeUtils.java index 85b900a5e6..6bff833e99 100755 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/EventTypeUtils.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/EventTypeUtils.java @@ -95,6 +95,12 @@ final public class EventTypeUtils { imageFileName = "artifact-icon.png"; } else if (typeID == TimelineEventType.WEB_FORM_ADDRESSES.getTypeID()) { imageFileName = "artifact-icon.png"; + } else if (typeID == TimelineEventType.METADATA_CREATED.getTypeID()) { + imageFileName = "blue-document-attribute-b.png"; + } else if (typeID == TimelineEventType.METADATA_LAST_SAVED.getTypeID()) { + imageFileName = "blue-document-attribute-m.png"; + } else if (typeID == TimelineEventType.METADATA_LAST_PRINTED.getTypeID()) { + imageFileName = "blue-document.png"; }else { imageFileName = "timeline_marker.png"; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 9416bca7d4..09e553a18a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -19,13 +19,18 @@ package org.sleuthkit.autopsy.keywordsearch; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.io.CharSource; import java.io.IOException; import java.io.Reader; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.List; +import static java.util.Locale.US; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -120,28 +125,24 @@ public final class KeywordSearchIngestModule implements FileIngestModule { "application/x-z", //NON-NLS "application/x-compress"); //NON-NLS - private static final List METADATA_TYPES + private static final List METADATA_DATE_TYPES = ImmutableList.of( - "Total-Time", //NON-NLS - "Template", //NON-NLS - "Revision-Number", //NON-NLS "Last-Save-Date", //NON-NLS "Last-Printed", //NON-NLS - "Last-Author", //NON-NLS - "Edit-Time", //NON-NLS - "Creation-Date", //NON-NLS - "Company", //NON-NLS - "Author", //NON-NLS - "Application-Name", //NON-NLS - "protected", //NON-NLS - "SourceModified", //NON-NLS - "Last-Modified", //NON-NLS - "Producer", //NON-NLS - "pdf:docinfo:creator_tool", //NON-NLS - "Title", //NON-NLS - "pdf:encrypted", //NON-NLS - "Description", //NON-NLS - "pdf:PDFVersion"); //NON-NLS + "Creation-Date"); //NON-NLS + + private static final Map METADATA_TYPES_MAP = ImmutableMap.builder() + .put("Last-Save-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED) + .put("Last-Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID) + .put("Creation-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED) + .put("Company", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ORGANIZATION) + .put("Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OWNER) + .put("Application-Name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME) + .put("Last-Printed", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LAST_PRINTED_DATETIME) + .put("Producer", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME) + .put("Title", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION) + .put("pdf:PDFVersion", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VERSION) + .build(); /** @@ -552,31 +553,49 @@ public final class KeywordSearchIngestModule implements FileIngestModule { private void createMetadataArtifact(AbstractFile aFile, Map metadata) { String moduleName = KeywordSearchIngestModule.class.getName(); + Collection attributes = new ArrayList<>(); Collection bbartifacts = new ArrayList<>(); for (Map.Entry entry : metadata.entrySet()) { - if (METADATA_TYPES.contains(entry.getKey())) { - if (!entry.getValue().isEmpty() && !entry.getValue().contentEquals(" ")) { - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, entry.getKey())); - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE, moduleName, entry.getValue())); - try { - BlackboardArtifact bbart = aFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA); - bbart.addAttributes(attributes); - bbartifacts.add(bbart); - } catch (TskCoreException ex) { - // return and continue processing - return; + if (METADATA_TYPES_MAP.containsKey(entry.getKey())) { + if (!entry.getValue().isEmpty() && !entry.getValue().startsWith(" ")) { + if (METADATA_DATE_TYPES.contains(entry.getKey())) { + SimpleDateFormat metadataDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", US); + Long metadataDateTime = Long.valueOf(0); + try { + String metadataDate = entry.getValue().replaceAll("T"," ").replaceAll("Z", ""); + Date usedDate = metadataDateFormat.parse(metadataDate); + metadataDateTime = usedDate.getTime()/1000; + attributes.add(new BlackboardAttribute(METADATA_TYPES_MAP.get(entry.getKey()), moduleName, metadataDateTime)); + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed then will continue on. + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for metadata attribute %s.", entry.getValue(), entry.getKey()), ex); //NON-NLS + continue; + } + } else { + attributes.add(new BlackboardAttribute(METADATA_TYPES_MAP.get(entry.getKey()), moduleName, entry.getValue())); } } } } - if (!bbartifacts.isEmpty()) { - try{ - Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().postArtifacts(bbartifacts, moduleName); - } catch (NoCurrentCaseException | Blackboard.BlackboardException ex) { - // Ignore this and continue on - //logger.log(Level.SEVERE, "Unable to post blackboard artifacts", ex); //NON-NLS - return; + if (!attributes.isEmpty()) { + try { + BlackboardArtifact bbart = aFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA); + bbart.addAttributes(attributes); + bbartifacts.add(bbart); + } catch (TskCoreException ex) { + // Log error and return to continue processing + logger.log(Level.WARNING, String.format("Error creatinkg or adding artifact."), ex); //NON-NLS + return; + } + if (!bbartifacts.isEmpty()) { + try{ + Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().postArtifacts(bbartifacts, moduleName); + } catch (NoCurrentCaseException | Blackboard.BlackboardException ex) { + // Log error and return to continue processing + logger.log(Level.WARNING, "Unable to post blackboard artifacts", ex); //NON-NLS + return; + } } } } From c48eb1a387584c37e3e4557aa11bb722918d5947 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Fri, 29 May 2020 15:10:45 -0400 Subject: [PATCH 04/18] Update KeywordSearchIngestModule.java Codacy Changes --- .../KeywordSearchIngestModule.java | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 09e553a18a..a7f29083f7 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -558,23 +558,9 @@ public final class KeywordSearchIngestModule implements FileIngestModule { Collection bbartifacts = new ArrayList<>(); for (Map.Entry entry : metadata.entrySet()) { if (METADATA_TYPES_MAP.containsKey(entry.getKey())) { - if (!entry.getValue().isEmpty() && !entry.getValue().startsWith(" ")) { - if (METADATA_DATE_TYPES.contains(entry.getKey())) { - SimpleDateFormat metadataDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", US); - Long metadataDateTime = Long.valueOf(0); - try { - String metadataDate = entry.getValue().replaceAll("T"," ").replaceAll("Z", ""); - Date usedDate = metadataDateFormat.parse(metadataDate); - metadataDateTime = usedDate.getTime()/1000; - attributes.add(new BlackboardAttribute(METADATA_TYPES_MAP.get(entry.getKey()), moduleName, metadataDateTime)); - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed then will continue on. - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for metadata attribute %s.", entry.getValue(), entry.getKey()), ex); //NON-NLS - continue; - } - } else { - attributes.add(new BlackboardAttribute(METADATA_TYPES_MAP.get(entry.getKey()), moduleName, entry.getValue())); - } + BlackboardAttribute bba = checkAttribute(entry.getKey(), entry.getValue()); + if (bba != null) { + attributes.add(bba); } } } @@ -599,7 +585,34 @@ public final class KeywordSearchIngestModule implements FileIngestModule { } } } + + private BlackboardAttribute checkAttribute(String key, String value) { + String moduleName = KeywordSearchIngestModule.class.getName(); + if (!value.isEmpty() && value.charAt(0) != ' ') { + if (METADATA_DATE_TYPES.contains(key)) { + SimpleDateFormat metadataDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", US); + Long metadataDateTime = Long.valueOf(0); + try { + String metadataDate = value.replaceAll("T"," ").replaceAll("Z", ""); + Date usedDate = metadataDateFormat.parse(metadataDate); + metadataDateTime = usedDate.getTime()/1000; + return new BlackboardAttribute(METADATA_TYPES_MAP.get(key), moduleName, metadataDateTime); + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed then will continue on. + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for metadata attribute %s.", value, key), ex); //NON-NLS + return null; + } + } else { + return new BlackboardAttribute(METADATA_TYPES_MAP.get(key), moduleName, value); + } + } + + return null; + + } + + /** * Pretty print the text extractor metadata. * From 9e7f12eb8b91d5adc35cc4da828eb055af849abd Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 2 Jun 2020 10:16:55 -0400 Subject: [PATCH 05/18] Update KeywordSearchIngestModule.java Fix typo --- .../autopsy/keywordsearch/KeywordSearchIngestModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index a7f29083f7..ff93478d8e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -571,7 +571,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { bbartifacts.add(bbart); } catch (TskCoreException ex) { // Log error and return to continue processing - logger.log(Level.WARNING, String.format("Error creatinkg or adding artifact."), ex); //NON-NLS + logger.log(Level.WARNING, String.format("Error creating or adding artifact."), ex); //NON-NLS return; } if (!bbartifacts.isEmpty()) { From a5e5a3d5c5fe0580389ef4e104421235fade93a5 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 3 Jun 2020 16:07:40 -0400 Subject: [PATCH 06/18] Added persona information to CVT tooltip --- .../AccountDeviceInstanceNode.java | 50 ++++++++-- .../communications/CVTPersonaCache.java | 95 +++++++++++++++++++ 2 files changed, 136 insertions(+), 9 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 8bf29e70ac..96386c0e59 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -20,12 +20,17 @@ package org.sleuthkit.autopsy.communications; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; import javax.swing.Action; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -36,11 +41,15 @@ import org.sleuthkit.datamodel.CommunicationsManager; * Node to represent an Account Device Instance in the CVT */ final class AccountDeviceInstanceNode extends AbstractNode { - + + private static final Logger logger = Logger.getLogger(AccountDeviceInstanceNode.class.getName()); + private final AccountDeviceInstanceKey accountDeviceInstanceKey; private final CommunicationsManager commsManager; private final Account account; - + + private static final String TOOLTIP_TEMPLATE = "Contact: %s - Persona: %s"; + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; @@ -51,27 +60,27 @@ final class AccountDeviceInstanceNode extends AbstractNode { String iconPath = Utils.getIconFilePath(account.getAccountType()); this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); } - + AccountDeviceInstance getAccountDeviceInstance() { return accountDeviceInstanceKey.getAccountDeviceInstance(); } - + AccountDeviceInstanceKey getAccountDeviceInstanceKey() { return accountDeviceInstanceKey; } - + CommunicationsManager getCommsManager() { return commsManager; } - + long getMessageCount() { return accountDeviceInstanceKey.getMessageCount(); } - + CommunicationsFilter getFilter() { return accountDeviceInstanceKey.getCommunicationsFilter(); } - + @Override @NbBundle.Messages(value = {"AccountNode.device=Device", "AccountNode.accountName=Account", "AccountNode.accountType=Type", "AccountNode.messageCount=Items"}) protected Sheet createSheet() { @@ -95,7 +104,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { accountDeviceInstanceKey.getDataSourceName())); // NON-NLS return sheet; } - + @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); @@ -103,4 +112,27 @@ final class AccountDeviceInstanceNode extends AbstractNode { actions.add(ResetAndPinAccountsAction.getInstance()); return actions.toArray(new Action[actions.size()]); } + + @Override + public String getShortDescription() { + List personaList; + try { + personaList = CVTPersonaCache.getInstance().getPersonaAccounts(getName()); + } catch (ExecutionException ex) { + logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); + return getDisplayName(); + } + + String personaName; + if (!personaList.isEmpty()) { + personaName = personaList.get(0).getPersona().getName(); + if (personaList.size() > 1) { + personaName += String.format("(1 of %d)", personaList.size()); + } + } else { + personaName = "None"; + } + + return String.format(TOOLTIP_TEMPLATE, getDisplayName(), personaName); + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java new file mode 100755 index 0000000000..d4d36fa64d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -0,0 +1,95 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * A singleton cache of the PersonaAccount information. The list of + * PersonaAccounts for a given Account typeSpecificID retrieved on first access + * and evicted from the cache after 5 minutes. + */ +class CVTPersonaCache { + + private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName()); + private final LoadingCache> accountMap; + + private static CVTPersonaCache instance; + + /** + * Cache constructor. + */ + private CVTPersonaCache() { + accountMap = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build( + new CacheLoader>() { + @Override + public List load(String key) { + List accountList = new ArrayList<>(); + try { + if(CentralRepository.isEnabled()) { + Collection accounts = PersonaAccount.getPersonaAccountsForIdentifierLike(key); + accountList.addAll(accounts); + } + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, String.format("Unable to load Persona information for account: %s", key), ex); + } + return accountList; + } + } + ); + } + + /** + * Returns the singleton instance of the cache. + * + * @return CVTPersonaCache instance. + */ + static CVTPersonaCache getInstance() { + if (instance == null) { + instance = new CVTPersonaCache(); + } + + return instance; + } + + /** + * Returns the list of PersonaAccounts for the given Account typeSpecificId. + * + * @param typeSpecificID Account typeSpecificId. + * + * @return List of PersonaAccounts for id or empty list if none were found. + * + * @throws ExecutionException + */ + List getPersonaAccounts(String typeSpecificID) throws ExecutionException { + return accountMap.get(typeSpecificID); + } +} From 95451cbb5f8f6c277dbe52a6ded1b7a8e77df360 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 4 Jun 2020 09:29:22 -0400 Subject: [PATCH 07/18] Update KeywordSearchIngestModule.java Added better warning message to include the file name when there is an error for debugging if needed. --- .../KeywordSearchIngestModule.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index ff93478d8e..a403b32e7b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -132,16 +132,16 @@ public final class KeywordSearchIngestModule implements FileIngestModule { "Creation-Date"); //NON-NLS private static final Map METADATA_TYPES_MAP = ImmutableMap.builder() - .put("Last-Save-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED) - .put("Last-Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID) - .put("Creation-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED) - .put("Company", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ORGANIZATION) - .put("Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OWNER) - .put("Application-Name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME) - .put("Last-Printed", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LAST_PRINTED_DATETIME) - .put("Producer", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME) - .put("Title", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION) - .put("pdf:PDFVersion", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VERSION) + .put("Last-Save-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED) + .put("Last-Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID) + .put("Creation-Date", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED) + .put("Company", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ORGANIZATION) + .put("Author", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OWNER) + .put("Application-Name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME) + .put("Last-Printed", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LAST_PRINTED_DATETIME) + .put("Producer", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME) + .put("Title", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION) + .put("pdf:PDFVersion", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VERSION) .build(); @@ -571,7 +571,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { bbartifacts.add(bbart); } catch (TskCoreException ex) { // Log error and return to continue processing - logger.log(Level.WARNING, String.format("Error creating or adding artifact."), ex); //NON-NLS + logger.log(Level.WARNING, String.format("Error creating or adding metadata artifact for file %s.", aFile.getParentPath() + aFile.getName()), ex); //NON-NLS return; } if (!bbartifacts.isEmpty()) { @@ -579,7 +579,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard().postArtifacts(bbartifacts, moduleName); } catch (NoCurrentCaseException | Blackboard.BlackboardException ex) { // Log error and return to continue processing - logger.log(Level.WARNING, "Unable to post blackboard artifacts", ex); //NON-NLS + logger.log(Level.WARNING, String.format("Unable to post blackboard artifacts for file $s.", aFile.getParentPath() + aFile.getName()) , ex); //NON-NLS return; } } From 827561fd7ab27875333d17716110fbb44bc94e4b Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Thu, 4 Jun 2020 10:03:40 -0400 Subject: [PATCH 08/18] 6398 Persona deletion functionality --- .../centralrepository/datamodel/Persona.java | 14 ++++++- .../persona/Bundle.properties | 1 + .../persona/PersonaDetailsPanel.java | 6 +-- .../persona/PersonaManagerTopComponent.form | 12 +++++- .../persona/PersonaManagerTopComponent.java | 39 +++++++++++++++++-- 5 files changed, 63 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java index 0111e82a74..45468fb3ff 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java @@ -293,6 +293,14 @@ public class Persona { public void removeAccount(PersonaAccount account) throws CentralRepoException { PersonaAccount.removePersonaAccount(account.getId()); } + + /** + * Marks this persona as deleted + */ + public void delete() throws CentralRepoException { + String deleteSQL = "UPDATE personas SET status_id = " + PersonaStatus.DELETED.status_id + " WHERE id = " + this.id; + CentralRepository.getInstance().executeUpdateSQL(deleteSQL); + } /** * Callback to process a Persona query from the persona table. @@ -364,7 +372,8 @@ public class Persona { } /** - * Gets the rows from the Personas table with matching name. + * Gets the rows from the Personas table with matching name. + * Persona marked as DELETED are not returned. * * @param partialName Name substring to match. * @return Collection of personas matching the given name substring, may be @@ -376,7 +385,8 @@ public class Persona { public static Collection getPersonaByName(String partialName) throws CentralRepoException { String queryClause = PERSONA_QUERY - + "WHERE LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ; + + "WHERE p.status_id != " + PersonaStatus.DELETED.status_id + + " AND LOWER(p.name) LIKE " + "LOWER('%" + partialName + "%')" ; PersonaQueryCallback queryCallback = new PersonaQueryCallback(); CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties index f40bf2c7ca..24fdf1c0ff 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties @@ -53,3 +53,4 @@ AddAccountDialog.confidenceLbl.text=Confidence: AddAccountDialog.typeLbl.text=Type: AddAccountDialog.identiferLbl.text=Identifier: AddAccountDialog.identifierTextField.text= +PersonaManagerTopComponent.deleteBtn.text=Delete Persona diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java index 2c5c7b07f6..96b870994b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java @@ -560,7 +560,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { void clear() { currentPersona = null; - nameField.setText(Persona.getDefaultName()); + nameField.setText(mode == PersonaDetailsMode.CREATE ? Persona.getDefaultName() : ""); currentAccounts = new ArrayList<>(); currentMetadata = new ArrayList<>(); currentAliases = new ArrayList<>(); @@ -714,8 +714,8 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { } @Messages({ - "PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs two or more accounts", - "PersonaDetailsPanel_NotEnoughAccounts_Title=Not enough accounts", + "PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account", + "PersonaDetailsPanel_NotEnoughAccounts_Title=Missing account", "PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository", "PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure", "PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty", diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.form b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.form index f478739167..918e5e4302 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.form @@ -77,7 +77,8 @@ - + + @@ -110,6 +111,7 @@ + @@ -208,6 +210,14 @@ + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java index 7137dcb6c2..fa123dedc8 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java @@ -30,6 +30,7 @@ import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; +import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; @@ -54,7 +55,9 @@ public final class PersonaManagerTopComponent extends TopComponent { private Persona selectedPersona = null; @Messages({ - "PMTopComponent_Name=Persona Manager" + "PMTopComponent_Name=Persona Manager", + "PMTopComponent_delete_exception_Title=Delete failure", + "PMTopComponent_delete_exception_msg=Failed to delete persona", }) public PersonaManagerTopComponent() { initComponents(); @@ -83,6 +86,25 @@ public final class PersonaManagerTopComponent extends TopComponent { PersonaDetailsMode.CREATE, selectedPersona, new CreateEditCallbackImpl()); } }); + + deleteBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + if (selectedPersona != null) { + selectedPersona.delete(); + } + } catch (CentralRepoException ex) { + logger.log(Level.SEVERE, "Failed to delete persona: " + selectedPersona.getName(), ex); + JOptionPane.showMessageDialog(PersonaManagerTopComponent.this, + Bundle.PMTopComponent_delete_exception_msg(), + Bundle.PMTopComponent_delete_exception_Title(), + JOptionPane.ERROR_MESSAGE); + return; + } + executeSearch(); + } + }); // Results table resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -116,6 +138,7 @@ public final class PersonaManagerTopComponent extends TopComponent { Persona persona = currentResults.get(index); selectedPersona = persona; editBtn.setEnabled(true); + deleteBtn.setEnabled(true); } /** @@ -147,6 +170,8 @@ public final class PersonaManagerTopComponent extends TopComponent { if (selectedRow != -1) { setPersona(resultsTable.getSelectedRow()); detailsPanel.setMode(this, PersonaDetailsMode.VIEW, selectedPersona); + } else { + detailsPanel.clear(); } } @@ -189,6 +214,7 @@ public final class PersonaManagerTopComponent extends TopComponent { resultsTable.clearSelection(); updateResultsTable(results); editBtn.setEnabled(false); + deleteBtn.setEnabled(false); } @Override @@ -216,6 +242,7 @@ public final class PersonaManagerTopComponent extends TopComponent { searchBtn = new javax.swing.JButton(); editBtn = new javax.swing.JButton(); createBtn = new javax.swing.JButton(); + deleteBtn = new javax.swing.JButton(); detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel(); setMinimumSize(new java.awt.Dimension(400, 400)); @@ -246,6 +273,9 @@ public final class PersonaManagerTopComponent extends TopComponent { org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.createBtn.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonaManagerTopComponent.class, "PersonaManagerTopComponent.deleteBtn.text")); // NOI18N + deleteBtn.setEnabled(false); + javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel); searchPanel.setLayout(searchPanelLayout); searchPanelLayout.setHorizontalGroup( @@ -257,7 +287,8 @@ public final class PersonaManagerTopComponent extends TopComponent { .addComponent(createBtn) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(editBtn) - .addGap(0, 0, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(deleteBtn)) .addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addComponent(searchField) .addGroup(searchPanelLayout.createSequentialGroup() @@ -283,7 +314,8 @@ public final class PersonaManagerTopComponent extends TopComponent { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(editBtn) - .addComponent(createBtn)) + .addComponent(createBtn) + .addComponent(deleteBtn)) .addContainerGap()) ); @@ -304,6 +336,7 @@ public final class PersonaManagerTopComponent extends TopComponent { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton createBtn; + private javax.swing.JButton deleteBtn; private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel; private javax.swing.JButton editBtn; private javax.swing.JSplitPane jSplitPane1; From 90c74d2aaa32e77de374fa13df072c3ac9285d0e Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 4 Jun 2020 10:14:56 -0400 Subject: [PATCH 09/18] Fixed codacy issues and handled review comment --- .../communications/AccountDeviceInstanceNode.java | 14 +++++++++++--- .../autopsy/communications/CVTPersonaCache.java | 8 ++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 96386c0e59..c977cb81fc 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -28,6 +28,7 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; import org.sleuthkit.autopsy.coreutils.Logger; @@ -113,11 +114,18 @@ final class AccountDeviceInstanceNode extends AbstractNode { return actions.toArray(new Action[actions.size()]); } + @Messages({ + "# {0} - Contact Name", + "# {1} - Persona Name", + "AccountInstanceNode_Tooltip_Template=Contact: {0} - Persona: {1}", + "# {0} - PersonaAccount count", + "AccountInstanceNode_Tooltip_suffix=(1 of {0})" + }) @Override public String getShortDescription() { List personaList; try { - personaList = CVTPersonaCache.getInstance().getPersonaAccounts(getName()); + personaList = CVTPersonaCache.getPersonaAccounts(getName()); } catch (ExecutionException ex) { logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); return getDisplayName(); @@ -127,12 +135,12 @@ final class AccountDeviceInstanceNode extends AbstractNode { if (!personaList.isEmpty()) { personaName = personaList.get(0).getPersona().getName(); if (personaList.size() > 1) { - personaName += String.format("(1 of %d)", personaList.size()); + personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size())); } } else { personaName = "None"; } - return String.format(TOOLTIP_TEMPLATE, getDisplayName(), personaName); + return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java index d4d36fa64d..f4bface5fb 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -37,7 +37,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * PersonaAccounts for a given Account typeSpecificID retrieved on first access * and evicted from the cache after 5 minutes. */ -class CVTPersonaCache { +final class CVTPersonaCache { private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName()); private final LoadingCache> accountMap; @@ -72,7 +72,7 @@ class CVTPersonaCache { * * @return CVTPersonaCache instance. */ - static CVTPersonaCache getInstance() { + private static CVTPersonaCache getInstance() { if (instance == null) { instance = new CVTPersonaCache(); } @@ -89,7 +89,7 @@ class CVTPersonaCache { * * @throws ExecutionException */ - List getPersonaAccounts(String typeSpecificID) throws ExecutionException { - return accountMap.get(typeSpecificID); + static synchronized List getPersonaAccounts(String typeSpecificID) throws ExecutionException { + return getInstance().accountMap.get(typeSpecificID); } } From ec183142c01271cfa39d07ab755b7ff7e9c8e631 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 4 Jun 2020 11:05:35 -0400 Subject: [PATCH 10/18] Changed the name of the Tags Option panel to Custom Tags --- .../org/sleuthkit/autopsy/casemodule/services/Bundle.properties | 2 +- .../autopsy/casemodule/services/Bundle.properties-MERGED | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties index 833a778637..3050671087 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties @@ -1,4 +1,4 @@ -OptionsCategory_Name_TagNamesOptions=Tags +OptionsCategory_Name_TagNamesOptions=Custom Tags OptionsCategory_TagNames=TagNames TagNameDialog.title.text=New Tag TagNameDialog.JOptionPane.tagNameIllegalCharacters.message=Tag name may not contain any of the following symbols: \\ : * ? " < > | , ; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties-MERGED index 6f6c2374fb..c74380f5a2 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/Bundle.properties-MERGED @@ -1,4 +1,4 @@ -OptionsCategory_Name_TagNamesOptions=Tags +OptionsCategory_Name_TagNamesOptions=Custom Tags OptionsCategory_TagNames=TagNames TagNameDefinition.predefTagNames.bookmark.text=Bookmark TagNameDefinition.predefTagNames.followUp.text=Follow Up From a3bf0d8de24693711efa4f18964697dc9d8a0b26 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 4 Jun 2020 13:41:00 -0400 Subject: [PATCH 11/18] Fixed codacy issue --- .../AccountDeviceInstanceNode.java | 28 +++++++++---------- .../communications/CVTPersonaCache.java | 10 +++---- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index c977cb81fc..a567e6a31a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -42,15 +42,13 @@ import org.sleuthkit.datamodel.CommunicationsManager; * Node to represent an Account Device Instance in the CVT */ final class AccountDeviceInstanceNode extends AbstractNode { - + private static final Logger logger = Logger.getLogger(AccountDeviceInstanceNode.class.getName()); - + private final AccountDeviceInstanceKey accountDeviceInstanceKey; private final CommunicationsManager commsManager; private final Account account; - - private static final String TOOLTIP_TEMPLATE = "Contact: %s - Persona: %s"; - + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; @@ -61,27 +59,27 @@ final class AccountDeviceInstanceNode extends AbstractNode { String iconPath = Utils.getIconFilePath(account.getAccountType()); this.setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath); } - + AccountDeviceInstance getAccountDeviceInstance() { return accountDeviceInstanceKey.getAccountDeviceInstance(); } - + AccountDeviceInstanceKey getAccountDeviceInstanceKey() { return accountDeviceInstanceKey; } - + CommunicationsManager getCommsManager() { return commsManager; } - + long getMessageCount() { return accountDeviceInstanceKey.getMessageCount(); } - + CommunicationsFilter getFilter() { return accountDeviceInstanceKey.getCommunicationsFilter(); } - + @Override @NbBundle.Messages(value = {"AccountNode.device=Device", "AccountNode.accountName=Account", "AccountNode.accountType=Type", "AccountNode.messageCount=Items"}) protected Sheet createSheet() { @@ -105,7 +103,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { accountDeviceInstanceKey.getDataSourceName())); // NON-NLS return sheet; } - + @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); @@ -113,7 +111,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { actions.add(ResetAndPinAccountsAction.getInstance()); return actions.toArray(new Action[actions.size()]); } - + @Messages({ "# {0} - Contact Name", "# {1} - Persona Name", @@ -130,7 +128,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); return getDisplayName(); } - + String personaName; if (!personaList.isEmpty()) { personaName = personaList.get(0).getPersona().getName(); @@ -140,7 +138,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { } else { personaName = "None"; } - + return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java index f4bface5fb..b66fab6731 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -38,10 +38,10 @@ import org.sleuthkit.autopsy.coreutils.Logger; * and evicted from the cache after 5 minutes. */ final class CVTPersonaCache { - + private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName()); private final LoadingCache> accountMap; - + private static CVTPersonaCache instance; /** @@ -54,7 +54,7 @@ final class CVTPersonaCache { public List load(String key) { List accountList = new ArrayList<>(); try { - if(CentralRepository.isEnabled()) { + if (CentralRepository.isEnabled()) { Collection accounts = PersonaAccount.getPersonaAccountsForIdentifierLike(key); accountList.addAll(accounts); } @@ -72,11 +72,11 @@ final class CVTPersonaCache { * * @return CVTPersonaCache instance. */ - private static CVTPersonaCache getInstance() { + private static synchronized CVTPersonaCache getInstance() { if (instance == null) { instance = new CVTPersonaCache(); } - + return instance; } From 24432477b9adc47df3664a6592a9ea13b3fd15a3 Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Fri, 5 Jun 2020 10:31:47 -0400 Subject: [PATCH 12/18] 6398 Removing unused import --- .../centralrepository/persona/PersonaManagerTopComponent.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java index fa123dedc8..c2c7476496 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaManagerTopComponent.java @@ -30,7 +30,6 @@ import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; -import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; From 795fe87f72e37be3b035f10faed92c6d1242b725 Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Mon, 8 Jun 2020 11:15:09 -0400 Subject: [PATCH 13/18] 6398 setName and PR comments --- .../autopsy/centralrepository/datamodel/Persona.java | 6 ++++-- .../autopsy/centralrepository/datamodel/PersonaAccount.java | 6 ++++-- .../centralrepository/persona/PersonaDetailsPanel.java | 6 +++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java index 45468fb3ff..d5cd52c093 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/Persona.java @@ -680,7 +680,8 @@ public class Persona { for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) { String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType) - + " WHERE case_id = " + correlationCase.getID(); + + " WHERE case_id = " + correlationCase.getID() + + "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback(); CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); @@ -711,7 +712,8 @@ public class Persona { for (CentralRepoAccount.CentralRepoAccountType crAccountType : accountTypes) { String querySql = getPersonaFromInstanceTableQueryTemplate(crAccountType) - + " WHERE data_source_id = " + dataSource.getID(); + + " WHERE data_source_id = " + dataSource.getID() + + "AND personas.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); PersonaFromAccountInstanceQueryCallback queryCallback = new PersonaFromAccountInstanceQueryCallback(); CentralRepository.getInstance().executeSelectSQL(querySql, queryCallback); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java index 7aa3b81372..4eca682472 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java @@ -261,7 +261,8 @@ public class PersonaAccount { */ public static Collection getPersonaAccountsForAccount(long accountId) throws CentralRepoException { String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE - + " WHERE persona_accounts.account_id = " + accountId; + + " WHERE persona_accounts.account_id = " + accountId + + "AND p.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); @@ -282,7 +283,8 @@ public class PersonaAccount { */ public static Collection getPersonaAccountsForIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException { String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE - + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')"; + + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')" + + "AND p.status_id != " + Persona.PersonaStatus.DELETED.getStatusId(); PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java index 96b870994b..fe09684ff1 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java @@ -182,7 +182,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { } } return false; - } + } boolean addAccount(CentralRepoAccount account, String justification, Persona.Confidence confidence) { if (!accountExists(account)) { @@ -712,6 +712,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { } initializeFields(); } + + public void setName(String name) { + nameField.setText(name); + } @Messages({ "PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account", From 66489b325f04399f4c119eeffcbe346df7764ba1 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jun 2020 13:18:28 -0400 Subject: [PATCH 14/18] Added getPersonaAccountsForAccount --- .../datamodel/PersonaAccount.java | 132 ++++++++++++------ .../persona/Bundle.properties-MERGED | 73 ++++++++-- .../AccountDeviceInstanceNode.java | 2 +- .../communications/CVTPersonaCache.java | 13 +- 4 files changed, 159 insertions(+), 61 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java index 213a5f95c5..7e88739b66 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java @@ -24,13 +24,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Objects; +import org.sleuthkit.datamodel.Account; /** * This class represents an association between a Persona and an Account. - * + * * A Persona has at least one, possibly more, accounts associated with it. - * - * + * + * */ public class PersonaAccount { @@ -108,7 +109,6 @@ public class PersonaAccount { return Objects.equals(this.examiner, other.getExaminer()); } - /** * Callback to process a Persona Accounts query. */ @@ -166,7 +166,7 @@ public class PersonaAccount { } }; - // Query clause to select from persona_accounts table to create PersonaAccount(s) + // Query clause to select from persona_accounts table to create PersonaAccount(s) private static final String PERSONA_ACCOUNTS_QUERY_CALUSE = "SELECT justification, confidence_id, date_added, persona_accounts.examiner_id as pa_examiner_id, pa_examiner.login_name as pa_examiner_login_name, pa_examiner.display_name as pa_examiner_display_name," + " personas.id as persona_id, personas.uuid, personas.name, personas.comment, personas.created_date, personas.modified_date, personas.status_id, " + " personas.examiner_id as persona_examiner_id, persona_examiner.login_name as persona_examiner_login_name, persona_examiner.display_name as persona_examiner_display_name, " @@ -178,67 +178,111 @@ public class PersonaAccount { + " JOIN account_types as account_types on accounts.account_type_id = account_types.id " + " JOIN examiners as pa_examiner ON pa_examiner.id = persona_accounts.examiner_id " + " JOIN examiners as persona_examiner ON persona_examiner.id = personas.examiner_id "; - - - /** + + /** * Gets all the Accounts for the specified Persona. * * @param personaId Id of persona for which to get the accounts for. + * * @return Collection of PersonaAccounts, may be empty. * * @throws CentralRepoException If there is an error in getting the - * persona_account. + * persona_account. */ static Collection getPersonaAccountsForPersona(long personaId) throws CentralRepoException { - String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE - + " WHERE persona_accounts.persona_id = " + personaId; + CentralRepository cr = CentralRepository.getInstance(); - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + if (cr != null) { + String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE + + " WHERE persona_accounts.persona_id = " + personaId; - return queryCallback.getPersonaAccountsList(); + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); + + return queryCallback.getPersonaAccountsList(); + } + + return new ArrayList<>(); } - + /** * Gets all the Persona for the specified Account. * * @param accountId Id of account for which to get the Personas for. + * * @return Collection of PersonaAccounts. may be empty. * * @throws CentralRepoException If there is an error in getting the - * persona_account. + * persona_account. */ public static Collection getPersonaAccountsForAccount(long accountId) throws CentralRepoException { - String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE - + " WHERE persona_accounts.account_id = " + accountId; - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + CentralRepository cr = CentralRepository.getInstance(); - return queryCallback.getPersonaAccountsList(); + if (cr != null) { + String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE + + " WHERE persona_accounts.account_id = " + accountId; + + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); + + return queryCallback.getPersonaAccountsList(); + } + + return new ArrayList<>(); } - + /** * Gets all the Persona associated with all the accounts matching the given * account identifier substring. * * @param accountIdentifierSubstring Account identifier substring to search - * for. + * for. + * * @return Collection of PersonaAccounts. may be empty. * * @throws CentralRepoException If there is an error in getting the - * persona_account. + * persona_account. */ public static Collection getPersonaAccountsForIdentifierLike(String accountIdentifierSubstring) throws CentralRepoException { String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + accountIdentifierSubstring + "%')"; - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + CentralRepository cr = CentralRepository.getInstance(); + if (cr != null) { + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); - return queryCallback.getPersonaAccountsList(); + return queryCallback.getPersonaAccountsList(); + } + + return new ArrayList<>(); } - + + /** + * Gets all the Persona associated with the given account. + * + * @param account Account to search for. + * + * @return Collection of PersonaAccounts, maybe empty if none were found or + * CR is not enabled. + * + * @throws CentralRepoException + */ + public static Collection getPersonaAccountsForAccount(Account account) throws CentralRepoException { + String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE + + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER('%" + account.getTypeSpecificID() + "%') AND type_name = '" + account.getAccountType().getTypeName() + "' "; + + CentralRepository cr = CentralRepository.getInstance(); + if (cr != null) { + PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); + return queryCallback.getPersonaAccountsList(); + } + + return new ArrayList<>(); + } + /** * Callback to process a query that gets all accounts belonging to a * persona. @@ -274,21 +318,29 @@ public class PersonaAccount { * @param personaId Id of the persona to look for. * * @return Collection of all accounts associated with the given persona, may - * be empty. - * @throws CentralRepoException If there is an error in getting the accounts. + * be empty. + * + * @throws CentralRepoException If there is an error in getting the + * accounts. */ static Collection getAccountsForPersona(long personaId) throws CentralRepoException { - String queryClause = "SELECT account_id, " - + " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier," - + " account_types.type_name as type_name " - + " FROM persona_accounts " - + " JOIN accounts as accounts on persona_accounts.account_id = accounts.id " - + " JOIN account_types as account_types on accounts.account_type_id = account_types.id " - + " WHERE persona_accounts.persona_id = " + personaId; + CentralRepository cr = CentralRepository.getInstance(); - AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback(); - CentralRepository.getInstance().executeSelectSQL(queryClause, queryCallback); + if (cr != null) { + String queryClause = "SELECT account_id, " + + " accounts.account_type_id as account_type_id, accounts.account_unique_identifier as account_unique_identifier," + + " account_types.type_name as type_name " + + " FROM persona_accounts " + + " JOIN accounts as accounts on persona_accounts.account_id = accounts.id " + + " JOIN account_types as account_types on accounts.account_type_id = account_types.id " + + " WHERE persona_accounts.persona_id = " + personaId; - return queryCallback.getAccountsList(); + AccountsForPersonaQueryCallback queryCallback = new AccountsForPersonaQueryCallback(); + cr.executeSelectSQL(queryClause, queryCallback); + + return queryCallback.getAccountsList(); + } + + return new ArrayList<>(); } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED index 2258c7df21..3c7765f09c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED @@ -1,18 +1,38 @@ AddAccountDialog.title.text=Add Account +AddAccountDialog_dup_msg=This account is already added to the persona +AddAccountDialog_dup_Title=Account add failure +AddAccountDialog_empty_msg=The identifier field cannot be empty +AddAccountDialog_empty_Title=Empty identifier AddAccountDialog_get_types_exception_msg=Failed to access central repository AddAccountDialog_get_types_exception_Title=Central Repository failure -AddAccountDialog_validate_id_failure=Account ID must not be empty -AddAccountDialog_validate_id_failure_title=Account ID issue +AddAccountDialog_search_empty_msg=Account not found for given identifier and type +AddAccountDialog_search_empty_Title=Account not found +AddAccountDialog_search_failure_msg=Central Repository account search failed +AddAccountDialog_search_failure_Title=Account add failure +AddAliasDialog.title.text=Add Alias +AddAliasDialog_dup_msg=This alias has already been added to this persona +AddAliasDialog_dup_Title=Alias add failure +AddMetadataDialog.title.text=Add Metadata +AddMetadataDialog_dup_msg=A metadata entry with this name has already been added to this persona +AddMetadataDialog_dup_Title=Metadata add failure CTL_OpenPersonaManager=Persona Manager CTL_PersonaManagerTopComponentAction=Persona Manager CTL_PersonaDetailsTopComponent=Persona Details OpenPersonasAction.displayName=Persona Manager +PersonaDetailsDialogCreateTitle=Create Persona +PersonaDetailsDialogEditTitle=Edit Persona +PersonaDetailsPanel_CentralRepoErr_msg=Failure to write to Central Repository +PersonaDetailsPanel_CentralRepoErr_Title=Central Repository failure +PersonaDetailsPanel_EmptyName_msg=Persona name cannot be empty +PersonaDetailsPanel_EmptyName_Title=Empty persona name PersonaDetailsPanel_load_exception_msg=Failed to load persona PersonaDetailsPanel_load_exception_Title=Initialization failure PersonaDetailsPanel_NameCreate=Create Persona PersonaDetailsPanel_NameEdit=Edit Persona PersonaDetailsPanel_NameView=View Persona -PersonaManagerTopComponent.createBtn.text=Create New +PersonaDetailsPanel_NotEnoughAccounts_msg=Two or more accounts are necessary to create a persona +PersonaDetailsPanel_NotEnoughAccounts_Title=Not enough accounts +PersonaManagerTopComponent.createBtn.text=New Persona PersonaManagerTopComponent.searchBtn.text=Search PersonaManagerTopComponent.resultsTable.columnModel.title1=Name PersonaManagerTopComponent.resultsTable.columnModel.title0=ID @@ -20,6 +40,17 @@ PersonaManagerTopComponent.resultsTable.toolTipText= PersonaManagerTopComponent.searchAccountRadio.text=Account PersonaManagerTopComponent.searchNameRadio.text=Name PersonaManagerTopComponent.searchField.text= +AddAccountDialog.cancelBtn.text=Cancel +AddAccountDialog.okBtn.text=OK +PersonaManagerTopComponent.editBtn.text=Edit Persona +PersonaDetailsDialog.cancelBtn.text=Cancel +PersonaDetailsDialog.okBtn.text=OK +PersonaDetailsPanel.deleteCaseBtn.text=Delete +PersonaDetailsPanel.addCaseBtn.text=Add +PersonaDetailsPanel.casesLbl.text=Cases found in: +PersonaDetailsPanel.deleteAliasBtn.text=Delete +PersonaDetailsPanel.addAliasBtn.text=Add +PersonaDetailsPanel.aliasesLabel.text=Aliases: PersonaDetailsPanel.deleteMetadataBtn.text=Delete PersonaDetailsPanel.addMetadataBtn.text=Add PersonaDetailsPanel.metadataLabel.text=Metadata: @@ -27,18 +58,32 @@ PersonaDetailsPanel.deleteAccountBtn.text=Delete PersonaDetailsPanel.addAccountBtn.text=Add PersonaDetailsPanel.accountsLbl.text=Accounts: PersonaDetailsPanel.nameField.text= -PersonaDetailsPanel.saveBtn.toolTipText= -PersonaDetailsPanel.saveBtn.text=Save Changes PersonaDetailsPanel.nameLbl.text=Name: -PersonaDetailsPanel.deleteCaseBtn.text=Delete -PersonaDetailsPanel.addCaseBtn.text=Add -PersonaDetailsPanel.casesLbl.text=Cases found in: -PersonaDetailsPanel.deleteAliasBtn.text=Delete -PersonaDetailsPanel.addAliasBtn.text=Add -PersonaDetailsPanel.aliasesLabel.text=Aliases: -AddAccountDialog.cancelBtn.text=Cancel -AddAccountDialog.okBtn.text=OK -PersonaManagerTopComponent.editBtn.text=Edit +AddAliasDialog.accountsLbl.text=Account: +AddAliasDialog.okBtn.text=OK +AddAliasDialog.cancelBtn.text=Cancel +AddMetadataDialog.cancelBtn.text=Cancel +AddMetadataDialog.okBtn.text=OK +AddMetadataDialog.nameLbl.text=Name: +AddMetadataDialog.nameTextField.text= +AddMetadataDialog.valueLbl.text=Value: +AddMetadataDialog.valueTextField.text= +AddMetadataDialog.justificationLbl.text=Justification: +AddMetadataDialog.justificationTextField.text= +AddMetadataDialog.confidenceLbl.text=Confidence: +AddAliasDialog.justificationLbl.text=Justification: +AddAliasDialog.okBtn.text_1=OK +AddAliasDialog.cancelBtn.text_1=Cancel +AddAliasDialog.confidenceLbl.text=Confidence: +AddAliasDialog.justificationTextField.text= +AddAliasDialog.aliasLbl.text=Alias: +AddAliasDialog.aliasTextField.text= +AddAccountDialog.justificationTextField.text= +AddAccountDialog.justificationLbl.text=Justification: +AddAccountDialog.confidenceLbl.text=Confidence: +AddAccountDialog.typeLbl.text=Type: +AddAccountDialog.identiferLbl.text=Identifier: +AddAccountDialog.identifierTextField.text= PMTopComponent_Name=Persona Manager PMTopComponent_search_exception_msg=Failed to search personas PMTopComponent_search_exception_Title=Search failure diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index a567e6a31a..4a50969ef3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -123,7 +123,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { public String getShortDescription() { List personaList; try { - personaList = CVTPersonaCache.getPersonaAccounts(getName()); + personaList = CVTPersonaCache.getPersonaAccounts(account); } catch (ExecutionException ex) { logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); return getDisplayName(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java index b66fab6731..92cebad8a3 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTPersonaCache.java @@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Account; /** * A singleton cache of the PersonaAccount information. The list of @@ -40,7 +41,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; final class CVTPersonaCache { private static final Logger logger = Logger.getLogger(CVTPersonaCache.class.getName()); - private final LoadingCache> accountMap; + private final LoadingCache> accountMap; private static CVTPersonaCache instance; @@ -49,13 +50,13 @@ final class CVTPersonaCache { */ private CVTPersonaCache() { accountMap = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build( - new CacheLoader>() { + new CacheLoader>() { @Override - public List load(String key) { + public List load(Account key) { List accountList = new ArrayList<>(); try { if (CentralRepository.isEnabled()) { - Collection accounts = PersonaAccount.getPersonaAccountsForIdentifierLike(key); + Collection accounts = PersonaAccount.getPersonaAccountsForAccount(key); accountList.addAll(accounts); } } catch (CentralRepoException ex) { @@ -89,7 +90,7 @@ final class CVTPersonaCache { * * @throws ExecutionException */ - static synchronized List getPersonaAccounts(String typeSpecificID) throws ExecutionException { - return getInstance().accountMap.get(typeSpecificID); + static synchronized List getPersonaAccounts(Account account) throws ExecutionException { + return getInstance().accountMap.get(account); } } From 9aefb21a0639a292b8b145d63c859f3073ffdbb7 Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Tue, 9 Jun 2020 10:20:18 -0400 Subject: [PATCH 15/18] 6398 setName -> setPersonaName --- .../autopsy/centralrepository/persona/PersonaDetailsPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java index fe09684ff1..b9d2845e08 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java @@ -713,7 +713,7 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { initializeFields(); } - public void setName(String name) { + public void setPersonaName(String name) { nameField.setText(name); } From fb4005bed2162e8b861b3def535aadcdaad18e2c Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Tue, 9 Jun 2020 10:30:13 -0400 Subject: [PATCH 16/18] 6398 remove setPersonaName --- .../centralrepository/persona/PersonaDetailsPanel.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java index b9d2845e08..a7c76b7ad4 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java @@ -712,10 +712,6 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { } initializeFields(); } - - public void setPersonaName(String name) { - nameField.setText(name); - } @Messages({ "PersonaDetailsPanel_NotEnoughAccounts_msg=A persona needs at least one account", From 64630ef752aff5a0053b7b5ce43bd5c83164da36 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Tue, 9 Jun 2020 14:47:35 -0400 Subject: [PATCH 17/18] 6450: Remove auto-generation of personas --- .../eventlisteners/IngestEventsListener.java | 91 +------------------ 1 file changed, 1 insertion(+), 90 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 204aa502ac..d399fe4614 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -342,94 +342,7 @@ public class IngestEventsListener { dbManager = db; event = evt; } - - /** - * Automatically creates personas from all the TSK_CONTACT artifacts - * found in a data source. - * - * @param dataSource Data source that was just analyzed. - * @throws TskCoreException If there is any error getting contact - * artifacts from case database. - * @throws CentralRepoException If there is an error in creating - * personas in the Central Repo. - */ - private void autoGenerateContactPersonas(Content dataSource) throws TskCoreException, CentralRepoException { - - Blackboard blackboard; - try { - blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard(); - } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); - return; - } - - // get all TSK_CONTACT artifacts in this data source. - List contactArtifacts = blackboard.getArtifacts(TSK_CONTACT.getTypeID(), dataSource.getId()); - for (BlackboardArtifact artifact : contactArtifacts) { - - BlackboardAttribute nameAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME)); - String personaName = (nameAttr != null) ? nameAttr.getValueString() : null; - - // Get phone number and email attributes. - BlackboardAttribute phoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)); - BlackboardAttribute homePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME)); - BlackboardAttribute mobilePhoneAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE)); - BlackboardAttribute emailAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL)); - - Persona persona = personaFromContactAttribute(null, Account.Type.PHONE, phoneAttr, personaName); - persona = personaFromContactAttribute(persona, Account.Type.PHONE, homePhoneAttr, personaName); - persona = personaFromContactAttribute(persona, Account.Type.PHONE, mobilePhoneAttr, personaName); - personaFromContactAttribute(persona, Account.Type.EMAIL, emailAttr, personaName); - } - } - - - - /** - * Gets central repo account for the given attribute for a TSK_CONTACT - * artifact. Associates the given persona with that account. Creates a - * Persona, if one isn't provided. - * - * @param persona Persona to associate with the account. May be null, in - * which case a persona is created first. - * @param accountType Account type of account to be associated. - * @param attribute Attribute form which get the account id. - * @param personaName Persona name, if a persona needs to be created. - * @return Persona created or associated with the account. - * - * @throws TskCoreException If there is an error in normalizing the - * account id. - * @throws CentralRepoException If there is an erorr is getting the - * account or associating the persona with it. - */ - private Persona personaFromContactAttribute(Persona persona, Account.Type accountType, BlackboardAttribute attribute, String personaName) throws CentralRepoException, TskCoreException { - - Persona personaToReturn = persona; - if (attribute != null) { - - String accountId = attribute.getValueString(); - if (CommunicationsUtils.isValidAccountId(accountType, accountId)) { - if (accountType == Account.Type.PHONE) { - accountId = CommunicationsUtils.normalizePhoneNum(accountId); - } else if (accountType == Account.Type.EMAIL) { - accountId = CommunicationsUtils.normalizeEmailAddress(accountId); - } - - CentralRepoAccount.CentralRepoAccountType crAccountType = CentralRepository.getInstance().getAccountTypeByName(accountType.getTypeName()); - CentralRepoAccount crAccount = CentralRepository.getInstance().getOrCreateAccount(crAccountType, accountId); - - PersonaAccount personaAccount; - // If persona doesnt exist, create one - if (persona == null) { - personaToReturn = Persona.createPersonaForAccount(personaName, "Auto generated contact persona", Persona.PersonaStatus.UNKNOWN, crAccount, "Found in contact book entry", Persona.Confidence.DERIVED); - } else { - persona.addAccount(crAccount, "Found in contact book entry", Persona.Confidence.DERIVED); - } - } - } - return personaToReturn; - } - + @Override public void run() { // clear the tracker to reduce memory usage @@ -504,8 +417,6 @@ public class IngestEventsListener { correlationDataSource.setSha256(imageSha256Hash); } } - // automatically generate persona from contact artifacts. - autoGenerateContactPersonas(dataSource); } } catch (CentralRepoException ex) { LOGGER.log(Level.SEVERE, String.format( From a7f4c657b5fbb2b6049a91cfda7bd10944c555b8 Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Tue, 9 Jun 2020 15:14:21 -0400 Subject: [PATCH 18/18] 6398 Fixing merge --- .../autopsy/centralrepository/datamodel/PersonaAccount.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java index d652708b18..d0322ddbca 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java @@ -282,9 +282,6 @@ public class PersonaAccount { CentralRepository cr = CentralRepository.getInstance(); if (cr != null) { - String queryClause = PERSONA_ACCOUNTS_QUERY_CALUSE - + " WHERE persona_accounts.account_id = " + accountId; - PersonaAccountsQueryCallback queryCallback = new PersonaAccountsQueryCallback(); cr.executeSelectSQL(queryClause, queryCallback);