diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java index 1442a0ec62..1b37217355 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/RdbmsCentralRepo.java @@ -1503,24 +1503,27 @@ abstract class RdbmsCentralRepo implements CentralRepository { Long sourceObjID = instance.getFileObjectId(); //The CorrelationAttributeInstance will have a CorrelationCase, however that correlation case's ID will be null if the case is not in the CR. int correlationCaseId = instance.getCorrelationCase().getID(); + int correlationDataSourceId = instance.getCorrelationDataSource().getID(); String normalizedValue = CorrelationAttributeNormalizer.normalize(instance.getCorrelationType(), instance.getCorrelationValue()); Connection conn = connect(); PreparedStatement preparedStatement = null; String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(instance.getCorrelationType()); ResultSet resultSet = null; + try { - if (correlationCaseId > 0 && sourceObjID != null) { + if (correlationCaseId > 0 && sourceObjID != null && correlationDataSourceId > 0) { //The CorrelationCase is in the Central repository. String sql = "SELECT count(*) FROM (SELECT DISTINCT case_id FROM " //Get distinct cases with a matching value in the corresponding table from the central repository. + tableName - + " WHERE value=? AND NOT (file_obj_id=? AND case_id=?)) AS " //Check the file_obj_id AND case_id to ensure we ignore the currently selected instance. + + " WHERE value=? AND NOT (file_obj_id=? AND case_id=? AND data_source_id=?)) AS " //Check the file_obj_id AND case_id to ensure we ignore the currently selected instance. + tableName + "_other_case_count"; preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, normalizedValue); preparedStatement.setLong(2, sourceObjID); preparedStatement.setInt(3, correlationCaseId); + preparedStatement.setInt(4, correlationDataSourceId); } else { //The CorrelationCase is NOT in the central repository. String sql diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index 6081d0703c..c242128461 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -178,8 +178,8 @@ public abstract class AbstractAbstractFileNode extends A } else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) { ContentTagAddedEvent event = (ContentTagAddedEvent) evt; if (event.getAddedTag().getContent().equals(content)) { - List tags = this.getAllTagsFromDatabase(); - Pair scorePropAndDescr = getScorePropertyAndDescription(tags); + + Pair scorePropAndDescr = getScorePropertyAndDescription(); Score value = scorePropAndDescr.getLeft(); String descr = scorePropAndDescr.getRight(); List listWithJustFileAttr = new ArrayList<>(); @@ -188,14 +188,14 @@ public abstract class AbstractAbstractFileNode extends A listWithJustFileAttr.add(corrInstance); } updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), descr, value), - new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, listWithJustFileAttr)) + new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(getAllTagsFromDatabase(), listWithJustFileAttr)) ); } } else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) { ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt; if (event.getDeletedTagInfo().getContentID() == content.getId()) { List tags = getAllTagsFromDatabase(); - Pair scorePropAndDescr = getScorePropertyAndDescription(tags); + Pair scorePropAndDescr = getScorePropertyAndDescription(); Score value = scorePropAndDescr.getLeft(); String descr = scorePropAndDescr.getRight(); List listWithJustFileAttr = new ArrayList<>(); @@ -204,19 +204,18 @@ public abstract class AbstractAbstractFileNode extends A listWithJustFileAttr.add(corrInstance); } updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), descr, value), - new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, listWithJustFileAttr)) + new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(getAllTagsFromDatabase(), listWithJustFileAttr)) ); } } else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) { CommentChangedEvent event = (CommentChangedEvent) evt; if (event.getContentID() == content.getId()) { - List tags = getAllTagsFromDatabase(); List listWithJustFileAttr = new ArrayList<>(); CorrelationAttributeInstance corrInstance = CorrelationAttributeUtil.getCorrAttrForFile(content); if (corrInstance != null) { listWithJustFileAttr.add(corrInstance); } - updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, listWithJustFileAttr))); + updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(getAllTagsFromDatabase(), listWithJustFileAttr))); } } else if (eventType.equals(NodeSpecificEvents.TRANSLATION_AVAILABLE.toString())) { this.setDisplayName(evt.getNewValue().toString()); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 02b3279a19..80e60e6cc8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -327,15 +327,13 @@ public abstract class AbstractContentNode extends ContentNode /** * Returns Score property for the node. * - * @param tags list of tags. - * * @return Score property for the underlying content of the node. */ @Messages({ "# {0} - significanceDisplayName", "AbstractContentNode_getScorePropertyAndDescription_description=Has an {0} analysis result score" }) - protected Pair getScorePropertyAndDescription(List tags) { + protected Pair getScorePropertyAndDescription() { Score score = Score.SCORE_UNKNOWN; try { score = this.content.getAggregateScore(); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index cc32ae029f..a5bbc1770b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -1214,7 +1214,7 @@ public class BlackboardArtifactNode extends AbstractContentNode tags) { - Pair scoreAndDescription = getScorePropertyAndDescription(tags); + Pair scoreAndDescription = getScorePropertyAndDescription(); sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft())); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED index ae9867bc66..a28d9d9d83 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties-MERGED @@ -331,7 +331,10 @@ OpenReportAction.actionPerformed.ReportFileOpenPermissionDeniedMessage=Permissio OsAccount_listNode_name=OS Accounts OsAccounts.createSheet.comment.displayName=C OsAccounts.createSheet.comment.name=C +# {0} - occurrenceCount +OsAccounts.createSheet.count.description=There were {0} datasource(s) found with occurrences of the OS Account correlation value OsAccounts.createSheet.count.displayName=O +OsAccounts.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated OsAccounts.createSheet.count.name=O OsAccounts.createSheet.score.displayName=S OsAccounts.createSheet.score.name=S diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java index 60ad65c83f..e90352bdda 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java @@ -23,17 +23,21 @@ import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.events.AutopsyEvent; -import org.sleuthkit.datamodel.Tag; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeUtil; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AnalysisResult; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DataArtifact; +import org.sleuthkit.datamodel.OsAccount; +import org.sleuthkit.datamodel.OsAccountInstance; +import org.sleuthkit.datamodel.TskCoreException; /** * Background task to get Score, Comment and Occurrences values for an Abstract @@ -43,6 +47,7 @@ import org.sleuthkit.datamodel.DataArtifact; class GetSCOTask implements Runnable { private final WeakReference> weakNodeRef; + private static final Logger logger = Logger.getLogger(GetSCOTask.class.getName()); private final PropertyChangeListener listener; GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { @@ -55,19 +60,16 @@ class GetSCOTask implements Runnable { @Override public void run() { AbstractContentNode contentNode = weakNodeRef.get(); - //Check for stale reference or if columns are disabled if (contentNode == null || UserPreferences.getHideSCOColumns()) { return; } // get the SCO column values - List tags = contentNode.getAllTagsFromDatabase(); SCOData scoData = new SCOData(); - scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags)); + scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription()); //getting the correlation attribute and setting the comment column is done before the eamdb isEnabled check //because the Comment column will reflect the presence of comments in the CR when the CR is enabled, but reflect tag comments regardless String description = Bundle.GetSCOTask_occurrences_defaultDescription(); - List listOfPossibleAttributes = new ArrayList<>(); Content contentFromNode = contentNode.getContent(); if (contentFromNode instanceof AbstractFile) { @@ -76,10 +78,18 @@ class GetSCOTask implements Runnable { listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((AnalysisResult) contentFromNode)); } else if (contentFromNode instanceof DataArtifact) { listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch((DataArtifact) contentFromNode)); - } else { - //JIRA-TODO : add code for Jira-7938 OsAccounts + } else if (contentFromNode instanceof OsAccount) { + + try { + List osAccountInstances = ((OsAccount) contentFromNode).getOsAccountInstances(); + OsAccountInstance osAccountInstance = osAccountInstances.isEmpty() ? null : osAccountInstances.get(0); + listOfPossibleAttributes.addAll(CorrelationAttributeUtil.makeCorrAttrsForSearch(osAccountInstance)); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get OsAccountInstances for OsAccount with ID: " + contentFromNode.getId(), ex); + } } - scoData.setComment(contentNode.getCommentProperty(tags, listOfPossibleAttributes)); + + scoData.setComment(contentNode.getCommentProperty(contentNode.getAllTagsFromDatabase(), listOfPossibleAttributes)); CorrelationAttributeInstance corInstance = null; if (CentralRepository.isEnabled()) { if (listOfPossibleAttributes.size() > 1) { @@ -91,7 +101,6 @@ class GetSCOTask implements Runnable { } scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(corInstance, description)); } - // signal SCO data is available. if (listener != null) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java index 64db9b82eb..cc803f80fe 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java @@ -29,25 +29,34 @@ import java.util.List; import java.util.Optional; import java.util.logging.Level; import javax.swing.Action; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; import org.openide.util.Exceptions; +import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.WeakListeners; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.TimeZoneUtils; +import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.VALUE_LOADING; import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.OsAccountRealm; +import org.sleuthkit.datamodel.Score; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; @@ -207,6 +216,28 @@ public final class OsAccounts implements AutopsyVisitableItem { Bundle.OsAccounts_accountRealmNameProperty_desc(), realmNames.get(0))); } + } else if (evt.getPropertyName().equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) { + SCOData scoData = (SCOData) evt.getNewValue(); + if (scoData.getScoreAndDescription() != null) { + updateSheet(new NodeProperty<>( + Bundle.BlackboardArtifactNode_createSheet_score_name(), + Bundle.BlackboardArtifactNode_createSheet_score_displayName(), + scoData.getScoreAndDescription().getRight(), + scoData.getScoreAndDescription().getLeft())); + } + if (scoData.getComment() != null) { + updateSheet(new NodeProperty<>( + Bundle.BlackboardArtifactNode_createSheet_comment_name(), + Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), + NO_DESCR, scoData.getComment())); + } + if (scoData.getCountAndDescription() != null) { + updateSheet(new NodeProperty<>( + Bundle.BlackboardArtifactNode_createSheet_count_name(), + Bundle.BlackboardArtifactNode_createSheet_count_displayName(), + scoData.getCountAndDescription().getRight(), + scoData.getCountAndDescription().getLeft())); + } } } }; @@ -365,7 +396,7 @@ public final class OsAccounts implements AutopsyVisitableItem { @Override protected List getAllTagsFromDatabase() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + return new ArrayList<>(); } @Override @@ -415,5 +446,72 @@ public final class OsAccounts implements AutopsyVisitableItem { } } } + + @NbBundle.Messages({ + "OsAccounts.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated", + "# {0} - occurrenceCount", + "OsAccounts.createSheet.count.description=There were {0} datasource(s) found with occurrences of the OS Account correlation value"}) + @Override + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attributeInstance, String defaultDescription) { + Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting + String description = defaultDescription; + try { + //don't perform the query if there is no correlation value + if (attributeInstance != null && StringUtils.isNotBlank(attributeInstance.getCorrelationValue())) { + count = CentralRepository.getInstance().getCountCasesWithOtherInstances(attributeInstance); + description = Bundle.OsAccounts_createSheet_count_description(count); + } else if (attributeInstance != null) { + description = Bundle.OsAccounts_createSheet_count_hashLookupNotRun_description(); + } + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex); + } catch (CorrelationAttributeNormalizationException ex) { + logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex); + } + return Pair.of(count, description); + } + + /** + * Returns comment property for the node. + * + * @param tags The list of tags. + * @param attributes The list of correlation attribute instances. + * + * @return Comment property for the underlying content of the node. + */ + @Override + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, List attributes) { + + /* + * Has a tag with a comment been applied to the OsAccount or its + * source content? + */ + DataResultViewerTable.HasCommentStatus status = tags.size() > 0 ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT; + for (Tag tag : tags) { + if (!StringUtils.isBlank(tag.getComment())) { + status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT; + break; + } + } + + /* + * Does the given correlation attribute instance have a comment in + * the central repository? + */ + if (attributes != null && !attributes.isEmpty()) { + for (CorrelationAttributeInstance attribute : attributes) { + if (attribute != null && !StringUtils.isBlank(attribute.getComment())) { + if (status == DataResultViewerTable.HasCommentStatus.TAG_COMMENT) { + status = DataResultViewerTable.HasCommentStatus.CR_AND_TAG_COMMENTS; + } else { + status = DataResultViewerTable.HasCommentStatus.CR_COMMENT; + } + break; + } + } + } + + return status; + } } }