From b5faa7cea7afbbe0eceb58260dbddeb01c45c148 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 31 Mar 2022 11:46:26 -0400 Subject: [PATCH] commenting --- .../DirectoryTreeTopComponent.java | 2 +- .../mainui/datamodel/HostPersonDAO.java | 47 ++- .../mainui/datamodel/HostSearchParams.java | 7 + .../autopsy/mainui/datamodel/ReportsDAO.java | 13 + .../mainui/datamodel/ReportsRowDTO.java | 27 ++ .../mainui/datamodel/ReportsSearchParams.java | 3 + .../mainui/nodes/ChildNodeSelectionInfo.java | 7 + .../mainui/nodes/FileSystemFactory.java | 4 - .../autopsy/mainui/nodes/RootFactory.java | 298 ++++++++++++++++-- .../mainui/nodes/actions/ActionContext.java | 16 + .../mainui/nodes/actions/ActionsFactory.java | 77 +++++ 11 files changed, 453 insertions(+), 48 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index 643237bc49..c58fd6f4c3 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -1312,7 +1312,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat Node osAccountListNode = osAccountListNodeOpt.get(); if (osAccountListNode instanceof TreeNode) { - TreeNode treeNode = (TreeNode) osAccountListNode; + TreeNode treeNode = (TreeNode) osAccountListNode; treeNode.setNodeSelectionInfo(new OsAccountNodeSelectionInfo(osAccount.getId())); } diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java index 367ed40b3e..a5d1b512e1 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostPersonDAO.java @@ -48,13 +48,19 @@ public class HostPersonDAO extends AbstractDAO { private static HostPersonDAO instance = null; + /** + * @return The singleton instance of this class. + */ public static HostPersonDAO getInstance() { if (instance == null) { instance = new HostPersonDAO(); } return instance; } - + + /** + * @return Identifier used for unknown persons. + */ public static String getUnknownPersonsName() { return Bundle.HostPersonDAO_unknownPersons_displayName(); } @@ -63,6 +69,13 @@ public class HostPersonDAO extends AbstractDAO { return Case.getCurrentCaseThrows().getSleuthkitCase(); } + /** + * Returns tree items for all hosts in the case. + * + * @return All hosts in the case. + * + * @throws ExecutionException + */ public TreeResultsDTO getAllHosts() throws ExecutionException { try { return new TreeResultsDTO<>(getCase().getHostManager().getAllHosts().stream() @@ -73,12 +86,23 @@ public class HostPersonDAO extends AbstractDAO { } } + /** + * Queries for all hosts belonging to the person or all hosts without a + * person association if person parameter is null. + * + * @param person The person to which hosts belong to or null for hosts with + * no associated person. + * + * @return The results in tree item form. + * + * @throws ExecutionException + */ public TreeResultsDTO getHosts(Person person) throws ExecutionException { try { - List hosts = person == null - ? getCase().getPersonManager().getHostsWithoutPersons() + List hosts = person == null + ? getCase().getPersonManager().getHostsWithoutPersons() : getCase().getPersonManager().getHostsForPerson(person); - + return new TreeResultsDTO<>(hosts.stream() .map(h -> createHostTreeItem(h)) .collect(Collectors.toList())); @@ -87,19 +111,24 @@ public class HostPersonDAO extends AbstractDAO { } } + /** + * Returns all persons associated with the case. + * @return The person tree results. + * @throws ExecutionException + */ public TreeResultsDTO getAllPersons() throws ExecutionException { try { List persons = getCase().getPersonManager().getPersons(); - + List> personSearchParams = new ArrayList<>(); for (Person person : persons) { personSearchParams.add(createPersonTreeItem(person)); } - + if (!getCase().getPersonManager().getHostsWithoutPersons().isEmpty()) { personSearchParams.add(createPersonTreeItem(null)); } - + return new TreeResultsDTO<>(personSearchParams); } catch (TskCoreException | NoCurrentCaseException ex) { throw new ExecutionException("Error while fetching all hosts.", ex); @@ -115,7 +144,6 @@ public class HostPersonDAO extends AbstractDAO { TreeDisplayCount.NOT_SHOWN); } - private TreeItemDTO createPersonTreeItem(Person person) { return new TreeItemDTO<>( PersonSearchParams.getTypeId(), @@ -138,10 +166,9 @@ public class HostPersonDAO extends AbstractDAO { .map(caseEvent -> caseEvent.toString()) .collect(Collectors.toSet()); - @Override Set processEvent(PropertyChangeEvent evt) { - return caseEvents.contains(evt.getPropertyName()) + return caseEvents.contains(evt.getPropertyName()) ? Collections.singleton(new HostPersonEvent()) : Collections.emptySet(); } diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostSearchParams.java index 88ff53ab28..70015745a1 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostSearchParams.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/HostSearchParams.java @@ -33,10 +33,17 @@ public class HostSearchParams { private final Host host; + /** + * Main constructor. + * @param host The host. + */ public HostSearchParams(Host host) { this.host = host; } + /** + * @return The host. + */ public Host getHost() { return host; } diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsDAO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsDAO.java index bde395989b..ed6b6b08a0 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsDAO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsDAO.java @@ -48,6 +48,9 @@ public class ReportsDAO extends AbstractDAO { private static ReportsDAO instance = null; + /** + * @return A singleton instance of this class. + */ public static ReportsDAO getInstance() { if (instance == null) { instance = new ReportsDAO(); @@ -57,6 +60,7 @@ public class ReportsDAO extends AbstractDAO { private final Cache, SearchResultsDTO> cache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).expireAfterAccess(CACHE_DURATION, CACHE_DURATION_UNITS).build(); + private SleuthkitCase getCase() throws NoCurrentCaseException { return Case.getCurrentCaseThrows().getSleuthkitCase(); } @@ -97,6 +101,15 @@ public class ReportsDAO extends AbstractDAO { totalResultCount); } + /** + * Queries the case based on the report search params and returns search results. + * @param repSearchParams The report search params. + * @param startItem The paged starting item. + * @param maxResultsCount The maximum result count for the page. + * @return The search results. + * @throws ExecutionException + * @throws IllegalArgumentException + */ public SearchResultsDTO getReports(ReportsSearchParams repSearchParams, long startItem, Long maxResultsCount) throws ExecutionException, IllegalArgumentException { SearchParams searchParams = new SearchParams<>(repSearchParams, startItem, maxResultsCount); return cache.get(searchParams, () -> fetchReports(searchParams)); diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsRowDTO.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsRowDTO.java index 4f872e856b..281a446d0c 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsRowDTO.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsRowDTO.java @@ -51,6 +51,9 @@ public class ReportsRowDTO extends BaseRowDTO { private static final String TYPE_ID = "REPORTS"; + /** + * @return The type identifier of this class. + */ public static String getTypeIdForClass() { return TYPE_ID; } @@ -61,6 +64,15 @@ public class ReportsRowDTO extends BaseRowDTO { private final String reportFilePath; private final Report report; + /** + * Main constructor. + * @param report The report. + * @param id The report id. + * @param sourceModuleName The source module name. + * @param reportName The report name. + * @param createdTime The created time. + * @param reportFilePath The report file path. + */ public ReportsRowDTO(Report report, long id, String sourceModuleName, String reportName, Date createdTime, String reportFilePath) { super(ImmutableList.of(sourceModuleName, reportName, createdTime, reportFilePath), TYPE_ID, id); this.sourceModuleName = sourceModuleName; @@ -70,22 +82,37 @@ public class ReportsRowDTO extends BaseRowDTO { this.report = report; } + /** + * @return The source module name. + */ public String getSourceModuleName() { return sourceModuleName; } + /** + * @return The report name. + */ public String getReportName() { return reportName; } + /** + * @return The created time. + */ public Date getCreatedTime() { return createdTime; } + /** + * @return The report file path. + */ public String getReportFilePath() { return reportFilePath; } + /** + * @return The report. + */ public Report getReport() { return report; } diff --git a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsSearchParams.java b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsSearchParams.java index 0a64b3fb7f..017c951be7 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsSearchParams.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/datamodel/ReportsSearchParams.java @@ -36,6 +36,9 @@ public class ReportsSearchParams { private static ReportsSearchParams instance = null; + /** + * @return A singleton instance of this class. + */ public static ReportsSearchParams getInstance() { if (instance == null) { instance = new ReportsSearchParams(); diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/ChildNodeSelectionInfo.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/ChildNodeSelectionInfo.java index 6cded72c7a..50de052cd2 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/ChildNodeSelectionInfo.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/ChildNodeSelectionInfo.java @@ -75,10 +75,17 @@ public interface ChildNodeSelectionInfo { } } + /** + * The selection of an os account. + */ public class OsAccountNodeSelectionInfo implements ChildNodeSelectionInfo { private final long osAccountId; + /** + * Main constructor. + * @param osAccountId The os account id. + */ public OsAccountNodeSelectionInfo(long osAccountId) { this.osAccountId = osAccountId; } diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/FileSystemFactory.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/FileSystemFactory.java index f194b93edf..22fd36ab17 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/FileSystemFactory.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/FileSystemFactory.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.mainui.nodes; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.Collections; import java.util.Objects; import java.util.Optional; import org.openide.nodes.Children; @@ -28,7 +27,6 @@ import org.openide.nodes.Node; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.Action; -import javax.swing.SwingUtilities; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; @@ -37,7 +35,6 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.FileTypeExtensions; -import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.utils.FileNameTransTask; import org.sleuthkit.autopsy.directorytree.ExtractUnallocAction; import org.sleuthkit.autopsy.directorytree.FileSystemDetailsAction; @@ -55,7 +52,6 @@ import static org.sleuthkit.autopsy.mainui.nodes.NodeIconUtil.DELETED_FILE; import static org.sleuthkit.autopsy.mainui.nodes.NodeIconUtil.DELETED_FOLDER; import static org.sleuthkit.autopsy.mainui.nodes.NodeIconUtil.FOLDER; import static org.sleuthkit.autopsy.mainui.nodes.TreeNode.getDefaultLookup; -import org.sleuthkit.autopsy.mainui.nodes.actions.ActionContext; import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory; import org.sleuthkit.autopsy.texttranslation.TextTranslationService; import org.sleuthkit.datamodel.AbstractFile; diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java index 960dfa1fc8..3fff6b8c90 100644 --- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/RootFactory.java @@ -24,17 +24,23 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; +import org.openide.util.Lookup; import org.openide.util.NbBundle.Messages; import org.openide.util.WeakListeners; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CasePreferences; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.mainui.datamodel.FileSystemContentSearchParam; +import org.sleuthkit.autopsy.mainui.datamodel.FileSystemDAO; import org.sleuthkit.autopsy.mainui.datamodel.HostPersonDAO; import org.sleuthkit.autopsy.mainui.datamodel.HostSearchParams; import org.sleuthkit.autopsy.mainui.datamodel.MainDAO; @@ -48,7 +54,13 @@ import org.sleuthkit.autopsy.mainui.datamodel.events.DAOEvent; import org.sleuthkit.autopsy.mainui.datamodel.events.HostPersonEvent; import org.sleuthkit.autopsy.mainui.datamodel.events.TreeEvent; import org.sleuthkit.autopsy.mainui.nodes.TreeNode.StaticTreeNode; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.Host; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.Person; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; /** * @@ -56,14 +68,24 @@ import org.sleuthkit.datamodel.Person; */ public class RootFactory { + /** + * @return The root children to be displayed in the tree. + */ public static Children getRootChildren() { if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { return Children.create(new HostPersonRootFactory(), true); } else { - return new DefaultViewRootFactory(); + return new DefaultViewRootChildren(); } } + /** + * Returns a string of the safely converted long to be used in a name id. + * + * @param l The number or null. + * + * @return The safely stringified number. + */ private static String getLongString(Long l) { return l == null ? "" : l.toString(); } @@ -115,7 +137,7 @@ public class RootFactory { @SuppressWarnings("unchecked") protected Node createNodeForKey(TreeItemDTO key) { if (key.getSearchParams() instanceof HostSearchParams) { - return new HostNode((TreeItemDTO) key); + return HostNode.getPersonHostViewNode((TreeItemDTO) key); } else if (key.getSearchParams() instanceof PersonSearchParams) { return new PersonNode((TreeItemDTO) key); } else { @@ -124,9 +146,15 @@ public class RootFactory { } } - public static class DefaultViewRootFactory extends Children.Array { + /** + * The root children for the default view preference. + */ + public static class DefaultViewRootChildren extends Children.Array { - public DefaultViewRootFactory() { + /** + * Main constructor. + */ + public DefaultViewRootChildren() { super(Arrays.asList( new AllDataSourcesNode(), new ViewsRootNode(null), @@ -139,6 +167,9 @@ public class RootFactory { } } + /** + * Node in default view displaying all hosts/data sources. + */ @Messages({"RootFactory_AllDataSourcesNode_displayName=Data Sources"}) public static class AllDataSourcesNode extends StaticTreeNode { @@ -153,6 +184,9 @@ public class RootFactory { return NAME_ID; } + /** + * Main constructor. + */ public AllDataSourcesNode() { super(NAME_ID, Bundle.RootFactory_AllDataSourcesNode_displayName(), @@ -161,6 +195,9 @@ public class RootFactory { } } + /** + * A person node. + */ @Messages(value = {"PersonNode_unknownPersonNode_title=Unknown Persons"}) public static class PersonNode extends TreeNode { @@ -183,11 +220,16 @@ public class RootFactory { return Bundle.PersonNode_unknownPersonNode_title(); } + /** + * Main constructor. + * + * @param itemData The row data for the person. + */ public PersonNode(TreeResultsDTO.TreeItemDTO itemData) { super(PersonSearchParams.getTypeId() + getLongString( - itemData.getSearchParams().getPerson() == null - ? 0 - : itemData.getSearchParams().getPerson().getPersonId()), + itemData.getSearchParams().getPerson() == null + ? 0 + : itemData.getSearchParams().getPerson().getPersonId()), "org/sleuthkit/autopsy/images/person.png", itemData, Children.create(new HostFactory(itemData.getSearchParams().getPerson()), true), @@ -195,20 +237,43 @@ public class RootFactory { ? Lookups.fixed(itemData.getSearchParams(), itemData.getSearchParams().getPerson()) : Lookups.fixed(itemData.getSearchParams(), HostPersonDAO.getUnknownPersonsName())); } + + @Override + public Optional getPerson() { + return Optional.of(getItemData().getSearchParams().getPerson()); + } } + /** + * Factory displaying all hosts in default view. + */ public static class AllHostsFactory extends BaseHostFactory { @Override protected TreeResultsDTO getChildResults() throws IllegalArgumentException, ExecutionException { return MainDAO.getInstance().getHostPersonDAO().getAllHosts(); } + + @Override + protected TreeNode createNewNode(TreeItemDTO rowData) { + return HostNode.getDefaultViewNode(rowData); + } } + /** + * Factory displaying hosts belonging to a person (or null). + */ public static class HostFactory extends BaseHostFactory { private final Person parentPerson; + /** + * Main constructor. + * + * @param parentPerson The person whose hosts will be shown. Null + * indicates showing any host with no person + * associated. + */ public HostFactory(Person parentPerson) { this.parentPerson = parentPerson; } @@ -217,14 +282,17 @@ public class RootFactory { protected TreeResultsDTO getChildResults() throws IllegalArgumentException, ExecutionException { return MainDAO.getInstance().getHostPersonDAO().getHosts(parentPerson); } - } - - public abstract static class BaseHostFactory extends TreeChildFactory { @Override - protected TreeNode createNewNode(TreeResultsDTO.TreeItemDTO rowData) { - return new HostNode(rowData); + protected TreeNode createNewNode(TreeItemDTO rowData) { + return HostNode.getPersonHostViewNode(rowData); } + } + + /** + * Base factory for displaying hosts. + */ + public abstract static class BaseHostFactory extends TreeChildFactory { @Override protected void handleDAOAggregateEvent(DAOAggregateEvent aggEvt) { @@ -247,8 +315,11 @@ public class RootFactory { } } + /** + * Node for a host. + */ public static class HostNode extends TreeNode { - + /** * Returns the name prefix of this node. * @@ -257,17 +328,78 @@ public class RootFactory { public static final String getNamePrefix() { return HostSearchParams.getTypeId(); } - - public HostNode(TreeResultsDTO.TreeItemDTO itemData) { + + /** + * Returns a host node whose children will be used in the default view. + * + * @param itemData The data associated with the host. + * + * @return A host node. + */ + public static HostNode getDefaultViewNode(TreeResultsDTO.TreeItemDTO itemData) { + return new HostNode(itemData, Children.create(new FileSystemFactory(itemData.getSearchParams().getHost()), true)); + } + + /** + * Returns a host node whose children will be used in the person/host + * view. + * + * @param itemData The data associated with the host. + * + * @return A host node. + */ + public static HostNode getPersonHostViewNode(TreeResultsDTO.TreeItemDTO itemData) { + return new HostNode(itemData, Children.create(new FileSystemFactory(itemData.getSearchParams().getHost()), true)); + } + + /** + * Private constructor. + * + * @param itemData The data for the host. + * @param children The children to use with this host. + */ + private HostNode(TreeResultsDTO.TreeItemDTO itemData, Children children) { super(HostSearchParams.getTypeId() + "_" + getLongString(itemData.getSearchParams().getHost().getHostId()), "org/sleuthkit/autopsy/images/host.png", itemData, - Children.create(new FileSystemFactory(itemData.getSearchParams().getHost()), true), + children, Lookups.fixed(itemData.getSearchParams(), itemData.getSearchParams().getHost())); } + + @Override + public Optional getHost() { + return Optional.of(getItemData().getSearchParams().getHost()); + } } - public static class DataSourceGroupedNode extends StaticTreeNode { + /** + * The factory to use to create data source grouping nodes to display in the + * host/person view. + */ + public static class DataSourceGroupedFactory extends FileSystemFactory { + + /** + * Main constructor. + * + * @param host The parent host. + */ + public DataSourceGroupedFactory(Host host) { + super(host); + } + + @Override + protected TreeNode createNewNode(TreeItemDTO rowData) { + return DataSourceGroupedNode.getInstance(rowData); + } + + } + + /** + * A data source grouping node to display in host/person view. + */ + public static class DataSourceGroupedNode extends TreeNode { + + private static final Logger logger = Logger.getLogger(DataSourceGroupedNode.class.getName()); private static final String NAME_PREFIX = "DATA_SOURCE_GROUPED"; @@ -279,19 +411,57 @@ public class RootFactory { public static final String getNamePrefix() { return NAME_PREFIX; } - - public DataSourceGroupedNode(long dataSourceObjId, String dsName, boolean isImage) { - super(NAME_PREFIX + "_" + dataSourceObjId, - dsName, - isImage ? "org/sleuthkit/autopsy/images/image.png" : "org/sleuthkit/autopsy/images/fileset-icon-16.png", - new DataSourceGroupedFactory(dataSourceObjId)); + + /** + * Returns an instance of the data source grouping node. + * + * @param itemData The row data. + * + * @return The node. + */ + public static DataSourceGroupedNode getInstance(TreeItemDTO itemData) { + long dataSourceId = itemData.getSearchParams().getContentObjectId(); + try { + + DataSource ds = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(dataSourceId); + return (ds == null) ? null : new DataSourceGroupedNode(itemData, ds); + } catch (NoCurrentCaseException ex) { + // Case is likely closing + return null; + } catch (TskCoreException | TskDataException ex) { + logger.log(Level.SEVERE, "Error creating node from data source with ID: " + dataSourceId, ex); + return null; + } + } + + /** + * Private constructor. + * + * @param itemData The row data. + * @param dataSource The relevant data source instance. + */ + private DataSourceGroupedNode(TreeItemDTO itemData, DataSource dataSource) { + super(NAME_PREFIX + "_" + getLongString(dataSource.getId()), + dataSource instanceof Image + ? "org/sleuthkit/autopsy/images/image.png" + : "org/sleuthkit/autopsy/images/fileset-icon-16.png", + itemData, + new DataSourceGroupedChildren(dataSource.getId()), + Lookups.singleton(dataSource)); } } - // shows all content related to data sources - public static class DataSourceGroupedFactory extends Children.Array { + /** + * Shows all content related to a data source in host/person view. + */ + public static class DataSourceGroupedChildren extends Children.Array { - public DataSourceGroupedFactory(long dataSourceObjId) { + /** + * Main constructor. + * + * @param dataSourceObjId The data source object id. + */ + public DataSourceGroupedChildren(long dataSourceObjId) { super(Arrays.asList( new DataSourceFilesNode(dataSourceObjId), new ViewsRootNode(dataSourceObjId), @@ -304,6 +474,9 @@ public class RootFactory { } } + /** + * Node for showing data source files in person/host view. + */ @Messages({"RootFactory_DataSourceFilesNode_displayName=Data Source Files"}) public static class DataSourceFilesNode extends StaticTreeNode { @@ -317,7 +490,12 @@ public class RootFactory { public static final String getNamePrefix() { return NAME_PREFIX; } - + + /** + * Main constructor. + * + * @param dataSourceObjId The data source object id. + */ public DataSourceFilesNode(long dataSourceObjId) { super(NAME_PREFIX + "_" + getLongString(dataSourceObjId), Bundle.RootFactory_DataSourceFilesNode_displayName(), @@ -326,6 +504,9 @@ public class RootFactory { } } + /** + * Root node for displaying "View" for file types. + */ @Messages({"RootFactory_ViewsRootNode_displayName=Views"}) public static class ViewsRootNode extends StaticTreeNode { @@ -339,7 +520,13 @@ public class RootFactory { public static final String getNamePrefix() { return NAME_PREFIX; } - + + /** + * Main constructor. + * + * @param dataSourceObjId The data source object id or null for no + * filter. + */ public ViewsRootNode(Long dataSourceObjId) { super(NAME_PREFIX + "_" + getLongString(dataSourceObjId), Bundle.RootFactory_ViewsRootNode_displayName(), @@ -348,6 +535,9 @@ public class RootFactory { } } + /** + * Root node for "Data Artifacts" in the tree. + */ @Messages({"RootFactory_DataArtifactsRootNode_displayName=Data Artifacts"}) public static class DataArtifactsRootNode extends StaticTreeNode { @@ -361,7 +551,13 @@ public class RootFactory { public static final String getNamePrefix() { return NAME_PREFIX; } - + + /** + * Main constructor. + * + * @param dataSourceObjId The data source object id or null for no + * filter. + */ public DataArtifactsRootNode(Long dataSourceObjId) { super(NAME_PREFIX + "_" + getLongString(dataSourceObjId), Bundle.RootFactory_DataArtifactsRootNode_displayName(), @@ -370,6 +566,9 @@ public class RootFactory { } } + /** + * Root node for "Analysis Results" in the tree. + */ @Messages({"RootFactory_AnalysisResultsRootNode_displayName=Analysis Results"}) public static class AnalysisResultsRootNode extends StaticTreeNode { @@ -383,7 +582,13 @@ public class RootFactory { public static final String getNamePrefix() { return NAME_PREFIX; } - + + /** + * Main constructor. + * + * @param dataSourceObjId The data source object id or null for no + * filter. + */ public AnalysisResultsRootNode(Long dataSourceObjId) { super(NAME_PREFIX + "_" + getLongString(dataSourceObjId), Bundle.RootFactory_AnalysisResultsRootNode_displayName(), @@ -392,6 +597,10 @@ public class RootFactory { } } + + /** + * Root node for OS accounts in the tree. + */ @Messages({"RootFactory_OsAccountsRootNode_displayName=OS Accounts"}) public static class OsAccountsRootNode extends StaticTreeNode { @@ -405,9 +614,15 @@ public class RootFactory { public static final String getNamePrefix() { return NAME_PREFIX; } - + private final Long dataSourceObjId; + /** + * Main constructor. + * + * @param dataSourceObjId The data source object id or null for no + * filter. + */ public OsAccountsRootNode(Long dataSourceObjId) { super(NAME_PREFIX + "_" + getLongString(dataSourceObjId), Bundle.RootFactory_OsAccountsRootNode_displayName(), @@ -423,6 +638,10 @@ public class RootFactory { } + + /** + * Root node for tags in the tree. + */ @Messages({"RootFactory_TagsRootNode_displayName=Tags"}) public static class TagsRootNode extends StaticTreeNode { @@ -436,7 +655,13 @@ public class RootFactory { public static final String getNamePrefix() { return NAME_PREFIX; } - + + /** + * Main constructor. + * + * @param dataSourceObjId The data source object id or null for no + * filter. + */ public TagsRootNode(Long dataSourceObjId) { super(NAME_PREFIX + "_" + getLongString(dataSourceObjId), Bundle.RootFactory_TagsRootNode_displayName(), @@ -445,6 +670,10 @@ public class RootFactory { } } + + /** + * Root node for reports in the tree. + */ @Messages({"RootFactory_ReportsRootNode_displayName=Reports"}) public static class ReportsRootNode extends StaticTreeNode { @@ -458,7 +687,10 @@ public class RootFactory { public static final String getNameIdentifier() { return NAME_ID; } - + + /** + * Main constructor. + */ public ReportsRootNode() { super(NAME_ID, Bundle.RootFactory_ReportsRootNode_displayName(), diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionContext.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionContext.java index f2ce9cb13a..aa418c16d3 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionContext.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionContext.java @@ -26,6 +26,8 @@ import org.sleuthkit.autopsy.mainui.nodes.actions.ActionsFactory.ActionGroup; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Host; +import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.Report; /** @@ -270,4 +272,18 @@ public interface ActionContext { default Optional getReport() { return Optional.empty(); } + + /** + * @return The person relevant to the node if present. + */ + default Optional getPerson() { + return Optional.empty(); + } + + /** + * @return The host relevant to the node if present. + */ + default Optional getHost() { + return Optional.empty(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionsFactory.java b/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionsFactory.java index f2363e3cef..e04756c89f 100755 --- a/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionsFactory.java +++ b/Core/src/org/sleuthkit/autopsy/mainui/nodes/actions/ActionsFactory.java @@ -46,11 +46,18 @@ import org.sleuthkit.autopsy.actions.OpenReportAction; import org.sleuthkit.autopsy.actions.ReplaceContentTagAction; import org.sleuthkit.autopsy.actions.ViewArtifactAction; import org.sleuthkit.autopsy.actions.ViewOsAccountAction; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactItem; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; +import org.sleuthkit.autopsy.datamodel.hosts.AssociatePersonsMenuAction; +import org.sleuthkit.autopsy.datamodel.hosts.MergeHostMenuAction; +import org.sleuthkit.autopsy.datamodel.hosts.RemoveParentPersonAction; +import org.sleuthkit.autopsy.datamodel.persons.DeletePersonAction; +import org.sleuthkit.autopsy.datamodel.persons.EditPersonAction; import org.sleuthkit.autopsy.datasourcesummary.ui.ViewSummaryInformationAction; import org.sleuthkit.autopsy.directorytree.CollapseAction; import org.sleuthkit.autopsy.directorytree.ExportCSVAction; @@ -68,7 +75,9 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DataArtifact; +import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.OsAccount; +import org.sleuthkit.datamodel.Person; import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.TskCoreException; @@ -180,6 +189,10 @@ public final class ActionsFactory { .ifPresent(ag -> actionGroups.add(ag)); + getHostActions(actionContext).ifPresent(ag -> actionGroups.add(ag)); + + getPersonActions(actionContext).ifPresent(ag -> actionGroups.add(ag)); + List actionList = new ArrayList<>(); for (ActionGroup aGroup : actionGroups) { if (aGroup != null) { @@ -555,6 +568,70 @@ public final class ActionsFactory { return Bundle.ActionsFactory_getAssociatedTypeStr_associated(); } } + + /** + * Returns an action group of host actions if host is present in action + * context. Otherwise, returns empty. + * + * @param actionContext The action context. + * + * @return The action group or empty. + */ + private static Optional getHostActions(ActionContext actionContext) { + return actionContext.getHost() + .flatMap(host -> { + // if there is a host, then provide actions + if (host != null) { + List actionsList = new ArrayList<>(); + + // Add the appropriate Person action + Optional parent; + try { + parent = Case.getCurrentCaseThrows().getSleuthkitCase().getPersonManager().getPerson(host); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.WARNING, String.format("Error fetching parent person of host: %s", host.getName() == null ? "" : host.getName()), ex); + return Optional.empty(); + } + + // if there is a parent, only give option to remove parent person. + if (parent.isPresent()) { + actionsList.add(new RemoveParentPersonAction(host, parent.get())); + } else { + actionsList.add(new AssociatePersonsMenuAction(host)); + } + + // Add option to merge hosts + actionsList.add(new MergeHostMenuAction(host)); + + return Optional.of(new ActionGroup(actionsList)); + } else { + return Optional.empty(); + } + }); + } + + /** + * Returns an action group of person actions if person is present in action + * context. Otherwise, returns empty. + * + * @param actionContext The action context. + * + * @return The action group or empty. + */ + private static Optional getPersonActions(ActionContext actionContext) { + return actionContext.getPerson() + .flatMap(person -> { + if (person == null) { + return Optional.empty(); + } else { + return Optional.of(new ActionGroup(Arrays.asList( + new EditPersonAction(person), + new DeletePersonAction(person) + ))); + } + }); + } + /** * Represents a group of related actions.