From de21dd88c95ffe195a7682702b7beec9a56ca418 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Wed, 20 May 2020 14:42:19 -0400 Subject: [PATCH 01/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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 90c74d2aaa32e77de374fa13df072c3ac9285d0e Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 4 Jun 2020 10:14:56 -0400 Subject: [PATCH 08/11] 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 09/11] 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 10/11] 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 66489b325f04399f4c119eeffcbe346df7764ba1 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 8 Jun 2020 13:18:28 -0400 Subject: [PATCH 11/11] 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); } }