commenting

This commit is contained in:
Greg DiCristofaro 2022-03-31 11:46:26 -04:00
parent e8995a18b4
commit b5faa7cea7
11 changed files with 453 additions and 48 deletions

View File

@ -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()));
}

View File

@ -48,6 +48,9 @@ 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();
@ -55,6 +58,9 @@ public class HostPersonDAO extends AbstractDAO {
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<HostSearchParams> getAllHosts() throws ExecutionException {
try {
return new TreeResultsDTO<>(getCase().getHostManager().getAllHosts().stream()
@ -73,6 +86,17 @@ 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<HostSearchParams> getHosts(Person person) throws ExecutionException {
try {
List<Host> hosts = person == null
@ -87,6 +111,11 @@ public class HostPersonDAO extends AbstractDAO {
}
}
/**
* Returns all persons associated with the case.
* @return The person tree results.
* @throws ExecutionException
*/
public TreeResultsDTO<PersonSearchParams> getAllPersons() throws ExecutionException {
try {
List<Person> persons = getCase().getPersonManager().getPersons();
@ -115,7 +144,6 @@ public class HostPersonDAO extends AbstractDAO {
TreeDisplayCount.NOT_SHOWN);
}
private TreeItemDTO<PersonSearchParams> createPersonTreeItem(Person person) {
return new TreeItemDTO<>(
PersonSearchParams.getTypeId(),
@ -138,7 +166,6 @@ public class HostPersonDAO extends AbstractDAO {
.map(caseEvent -> caseEvent.toString())
.collect(Collectors.toSet());
@Override
Set<? extends DAOEvent> processEvent(PropertyChangeEvent evt) {
return caseEvents.contains(evt.getPropertyName())

View File

@ -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;
}

View File

@ -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<SearchParams<ReportsSearchParams>, 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<ReportsSearchParams> searchParams = new SearchParams<>(repSearchParams, startItem, maxResultsCount);
return cache.get(searchParams, () -> fetchReports(searchParams));

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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;

View File

@ -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<? extends HostSearchParams>) key);
return HostNode.getPersonHostViewNode((TreeItemDTO<? extends HostSearchParams>) key);
} else if (key.getSearchParams() instanceof PersonSearchParams) {
return new PersonNode((TreeItemDTO<? extends PersonSearchParams>) 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<PersonSearchParams> {
@ -183,6 +220,11 @@ public class RootFactory {
return Bundle.PersonNode_unknownPersonNode_title();
}
/**
* Main constructor.
*
* @param itemData The row data for the person.
*/
public PersonNode(TreeResultsDTO.TreeItemDTO<? extends PersonSearchParams> itemData) {
super(PersonSearchParams.getTypeId() + getLongString(
itemData.getSearchParams().getPerson() == null
@ -195,20 +237,43 @@ public class RootFactory {
? Lookups.fixed(itemData.getSearchParams(), itemData.getSearchParams().getPerson())
: Lookups.fixed(itemData.getSearchParams(), HostPersonDAO.getUnknownPersonsName()));
}
@Override
public Optional<Person> getPerson() {
return Optional.of(getItemData().getSearchParams().getPerson());
}
}
/**
* Factory displaying all hosts in default view.
*/
public static class AllHostsFactory extends BaseHostFactory {
@Override
protected TreeResultsDTO<? extends HostSearchParams> getChildResults() throws IllegalArgumentException, ExecutionException {
return MainDAO.getInstance().getHostPersonDAO().getAllHosts();
}
@Override
protected TreeNode<HostSearchParams> createNewNode(TreeItemDTO<? extends HostSearchParams> 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<? extends HostSearchParams> getChildResults() throws IllegalArgumentException, ExecutionException {
return MainDAO.getInstance().getHostPersonDAO().getHosts(parentPerson);
}
}
public abstract static class BaseHostFactory extends TreeChildFactory<HostSearchParams> {
@Override
protected TreeNode<HostSearchParams> createNewNode(TreeResultsDTO.TreeItemDTO<? extends HostSearchParams> rowData) {
return new HostNode(rowData);
protected TreeNode<HostSearchParams> createNewNode(TreeItemDTO<? extends HostSearchParams> rowData) {
return HostNode.getPersonHostViewNode(rowData);
}
}
/**
* Base factory for displaying hosts.
*/
public abstract static class BaseHostFactory extends TreeChildFactory<HostSearchParams> {
@Override
protected void handleDAOAggregateEvent(DAOAggregateEvent aggEvt) {
@ -247,6 +315,9 @@ public class RootFactory {
}
}
/**
* Node for a host.
*/
public static class HostNode extends TreeNode<HostSearchParams> {
/**
@ -258,16 +329,77 @@ public class RootFactory {
return HostSearchParams.getTypeId();
}
public HostNode(TreeResultsDTO.TreeItemDTO<? extends HostSearchParams> 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<? extends HostSearchParams> 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<? extends HostSearchParams> 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<? extends HostSearchParams> 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<Host> 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<FileSystemContentSearchParam> createNewNode(TreeItemDTO<? extends FileSystemContentSearchParam> rowData) {
return DataSourceGroupedNode.getInstance(rowData);
}
}
/**
* A data source grouping node to display in host/person view.
*/
public static class DataSourceGroupedNode extends TreeNode<FileSystemContentSearchParam> {
private static final Logger logger = Logger.getLogger(DataSourceGroupedNode.class.getName());
private static final String NAME_PREFIX = "DATA_SOURCE_GROUPED";
@ -280,18 +412,56 @@ public class RootFactory {
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<? extends FileSystemContentSearchParam> 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;
}
}
// shows all content related to data sources
public static class DataSourceGroupedFactory extends Children.Array {
/**
* Private constructor.
*
* @param itemData The row data.
* @param dataSource The relevant data source instance.
*/
private DataSourceGroupedNode(TreeItemDTO<? extends FileSystemContentSearchParam> 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));
}
}
public DataSourceGroupedFactory(long dataSourceObjId) {
/**
* Shows all content related to a data source in host/person view.
*/
public static class DataSourceGroupedChildren extends Children.Array {
/**
* 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 {
@ -318,6 +491,11 @@ public class RootFactory {
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 {
@ -340,6 +521,12 @@ public class RootFactory {
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 {
@ -362,6 +552,12 @@ public class RootFactory {
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 {
@ -384,6 +583,12 @@ public class RootFactory {
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 {
@ -408,6 +617,12 @@ public class RootFactory {
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 {
@ -437,6 +656,12 @@ public class RootFactory {
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 {
@ -459,6 +688,9 @@ public class RootFactory {
return NAME_ID;
}
/**
* Main constructor.
*/
public ReportsRootNode() {
super(NAME_ID,
Bundle.RootFactory_ReportsRootNode_displayName(),

View File

@ -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<Report> getReport() {
return Optional.empty();
}
/**
* @return The person relevant to the node if present.
*/
default Optional<Person> getPerson() {
return Optional.empty();
}
/**
* @return The host relevant to the node if present.
*/
default Optional<Host> getHost() {
return Optional.empty();
}
}

View File

@ -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<Action> actionList = new ArrayList<>();
for (ActionGroup aGroup : actionGroups) {
if (aGroup != null) {
@ -556,6 +569,70 @@ public final class ActionsFactory {
}
}
/**
* 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<ActionGroup> getHostActions(ActionContext actionContext) {
return actionContext.getHost()
.flatMap(host -> {
// if there is a host, then provide actions
if (host != null) {
List<Action> actionsList = new ArrayList<>();
// Add the appropriate Person action
Optional<Person> 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 ? "<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<ActionGroup> 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.
*/