From 6853f79bccb46751fbd23ca065549968c8c1ab40 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 28 Jan 2021 11:15:49 -0500 Subject: [PATCH] first draft --- .../datamodel/AutopsyTreeChildFactory.java | 314 +++++++++++++++--- .../datamodel/DisplayableItemNodeVisitor.java | 23 +- 2 files changed, 291 insertions(+), 46 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java index b8625b782e..527ac41bf1 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AutopsyTreeChildFactory.java @@ -23,13 +23,18 @@ import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -39,15 +44,14 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitVisitableItem; import org.sleuthkit.datamodel.TskCoreException; - /** * Child factory to create the top level children of the autopsy tree - * + * */ -public final class AutopsyTreeChildFactory extends ChildFactory.Detachable { - +public final class AutopsyTreeChildFactory extends ChildFactory.Detachable { + private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName()); - + /** * Listener for handling DATA_SOURCE_ADDED events. */ @@ -55,13 +59,13 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable dataSources, int idx) { + return dataSources.get(idx % dataSources.size()); + } + + private OwnerHostTree getTree(SleuthkitCase tskCase) throws TskCoreException { + List dataSources = tskCase.getDataSources(); + if (CollectionUtils.isEmpty(dataSources)) { + return new OwnerHostTree(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + } + + return new OwnerHostTree( + Arrays.asList( + new Owner("Owner1", Arrays.asList( + new Host("Host1.1", Arrays.asList(getDs(dataSources, 0), getDs(dataSources, 1))), + new Host("Host1.2", Collections.emptyList())), + Arrays.asList(getDs(dataSources,2)) + ), + new Owner("Owner1", Arrays.asList( + new Host("Host1.1", Arrays.asList(getDs(dataSources, 0), getDs(dataSources, 1))), + new Host("Host2.3", Collections.emptyList())), + Arrays.asList(getDs(dataSources,3)) + ) + ), + Arrays.asList( + new Host("Host0.1", Arrays.asList(getDs(dataSources, 4))), + new Host("Host0.2", Arrays.asList(getDs(dataSources, 5))) + ), + Arrays.asList(getDs(dataSources, 6), getDs(dataSources, 7)) + ); + } + + private List getNodes(OwnerHostTree tree) { + List owners = (tree == null) ? null : tree.getOwners(); + List hosts = (tree == null) ? null : tree.getHosts(); + List dataSources = (tree == null) ? null : tree.getDataSources(); + + if (CollectionUtils.isNotEmpty(owners)) { + List toReturn = owners.stream() + .filter(o -> o != null) + .sorted((a, b) -> StringUtils.compareIgnoreCase(a.getName(), b.getName())) + .map(o -> new OwnerNode(o)) + .collect(Collectors.toList()); + + if (CollectionUtils.isNotEmpty(hosts) || CollectionUtils.isNotEmpty(dataSources)) { + toReturn.add(OwnerNode.getUnknownOwner(hosts, dataSources)); + } + + return toReturn; + } else if (CollectionUtils.isNotEmpty(hosts)) { + List toReturn = hosts.stream() + .filter(h -> h != null) + .sorted((a, b) -> StringUtils.compareIgnoreCase(a.getName(), b.getName())) + .map(h -> new HostNode(h)) + .collect(Collectors.toList()); + + if (CollectionUtils.isNotEmpty(dataSources)) { + toReturn.add(HostNode.getUnknownHost(dataSources)); + } + + return toReturn; + } else { + Stream dataSourceSafe = dataSources == null ? Stream.empty() : dataSources.stream(); + return dataSourceSafe + .filter(d -> d != null) + .sorted((a, b) -> StringUtils.compareIgnoreCase(a.getName(), b.getName())) + .map(d -> new DataSourceGroupingNode(d)) + .collect(Collectors.toList()); + } + } /** - * Creates keys for the top level children. - * + * Creates keys for the top level children. + * * @param list list of keys created * @return true, indicating that the key list is complete */ @Override protected boolean createKeys(List list) { - try { SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - + if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - List dataSources = tskCase.getDataSources(); - - Collections.sort(dataSources, new Comparator() { - @Override - public int compare(DataSource dataS1, DataSource dataS2) { - String dataS1Name = dataS1.getName().toLowerCase(); - String dataS2Name = dataS2.getName().toLowerCase(); - return dataS1Name.compareTo(dataS2Name); - } - }); - - List keys = new ArrayList<>(); - dataSources.forEach((datasource) -> { - keys.add(new DataSourceGrouping(datasource)); - }); - list.addAll(keys); - + list.addAll(getNodes(getTree(tskCase))); list.add(new Reports()); } else { List keys = new ArrayList<>(Arrays.asList( @@ -115,7 +174,6 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable hosts; + private final List dataSources; + + public Owner(String name, List hosts, List dataSources) { + this.name = name; + this.hosts = hosts; + this.dataSources = dataSources; + } + + public String getName() { + return name; + } + + public List getHosts() { + return hosts; + } + + public List getDataSources() { + return dataSources; + } + + } + + public static class Host { + private final String name; + private final List dataSources; + + public Host(String name, List dataSources) { + + this.name = name; + this.dataSources = (dataSources == null) ? Collections.emptyList() : Collections.unmodifiableList(new ArrayList(dataSources)); + } + + public List getDataSources() { + return this.dataSources; + } + + public String getName() { + return name; + } + } + + @Messages({ + "HostNode_unknownHostNode_title=Unknown Host" + }) + static class HostNode extends DisplayableItemNode { + + public static HostNode getUnknownHost(List dataSources) { + return new HostNode(null, dataSources); + } + + + + private static RootContentChildren getChildren(List dataSources) { + Stream dsNodes = (dataSources == null) + ? Stream.empty() + : dataSources.stream() + .filter(ds -> ds != null) + .sorted((a, b) -> StringUtils.compareIgnoreCase(a.getName(), b.getName())) + .map(d -> new DataSourceGroupingNode(d)); + + return new RootContentChildren(dsNodes.collect(Collectors.toList())); + } + + private HostNode(Host host, List dataSources) { + super(getChildren(dataSources), host == null ? null : Lookups.singleton(host)); + String safeName = (host == null || host.getName() == null) ? Bundle.HostNode_getUnknownHostNode_title() : host.getName(); + super.setName(safeName); + super.setDisplayName(safeName); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); + } + + HostNode(Host host) { + this(host, host == null ? null : host.getDataSources()); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + } + + private static class OwnerHostTree { + + private final List owners; + private final List hosts; + private final List dataSources; + + public OwnerHostTree(List owners, List hosts, List dataSources) { + this.owners = owners; + this.hosts = hosts; + this.dataSources = dataSources; + } + + public List getOwners() { + return owners; + } + + public List getHosts() { + return hosts; + } + + public List getDataSources() { + return dataSources; + } + + } + + @Messages({ + "OwnerNode_unknownHostNode_title=Unknown Owner" + }) + static class OwnerNode extends DisplayableItemNode { + + public static OwnerNode getUnknownOwner(List hosts, List dataSources) { + return new OwnerNode(null, hosts, dataSources); + } + + private static RootContentChildren getChildren(List hosts, List dataSources) { + List childNodes = (hosts == null) + ? Collections.emptyList() + : hosts.stream() + .filter(h -> h != null) + .sorted((a, b) -> StringUtils.compareIgnoreCase(a.getName(), b.getName())) + .map(h -> new HostNode(h)) + .collect(Collectors.toList()); + + if (CollectionUtils.isNotEmpty(hosts)) { + childNodes.add(HostNode.getUnknownHost(dataSources)); + } + + return new RootContentChildren(childNodes); + } + + OwnerNode(Owner owner) { + this(owner, (owner != null) ? owner.getHosts() : null, (owner != null) ? owner.getDataSources() : null); + } + + OwnerNode(Owner owner, List hosts, List dataSources) { + super(getChildren(hosts, dataSources), Lookups.singleton(owner)); + String safeName = (owner == null || owner.getName() == null) ? Bundle.OwnerNode_unknownHostNode_title() : owner.getName(); + super.setName(safeName); + super.setDisplayName(safeName); + this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/image.png"); + } + + @Override + public boolean isLeafTypeNode() { + return false; + } + + @Override + public T accept(DisplayableItemNodeVisitor visitor) { + return visitor.visit(this); + } + + @Override + public String getItemType() { + return getClass().getName(); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java index 24f437251f..84638e3723 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DisplayableItemNodeVisitor.java @@ -59,7 +59,7 @@ public interface DisplayableItemNodeVisitor { T visit(ImageNode in); T visit(VolumeNode vn); - + T visit(PoolNode pn); T visit(SlackFileNode sfn); @@ -192,7 +192,11 @@ public interface DisplayableItemNodeVisitor { * Attachments */ T visit(AttachmentNode node); - + + T visit(AutopsyTreeChildFactory.OwnerNode aThis); + + T visit(AutopsyTreeChildFactory.HostNode aThis); + /** * Visitor with an implementable default behavior for all types. Override * specific visit types to not use the default behavior. @@ -264,7 +268,7 @@ public interface DisplayableItemNodeVisitor { public T visit(ImageNode in) { return defaultVisit(in); } - + @Override public T visit(PoolNode pn) { return defaultVisit(pn); @@ -534,11 +538,20 @@ public interface DisplayableItemNodeVisitor { public T visit(Accounts.DefaultAccountTypeNode node) { return defaultVisit(node); } - + @Override public T visit(AttachmentNode node) { return defaultVisit(node); } - + + @Override + public T visit(AutopsyTreeChildFactory.HostNode node) { + return defaultVisit(node); + } + + @Override + public T visit(AutopsyTreeChildFactory.OwnerNode node) { + return defaultVisit(node); + } } }