first draft

This commit is contained in:
Greg DiCristofaro 2021-01-28 11:15:49 -05:00
parent 583400b73d
commit 6853f79bcc
2 changed files with 291 additions and 46 deletions

View File

@ -23,13 +23,18 @@ import java.beans.PropertyChangeListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.logging.Level; 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.ChildFactory;
import org.openide.nodes.Node; 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.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -39,7 +44,6 @@ import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitVisitableItem; import org.sleuthkit.datamodel.SleuthkitVisitableItem;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Child factory to create the top level children of the autopsy tree * Child factory to create the top level children of the autopsy tree
* *
@ -55,9 +59,9 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString()) && if (eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())
Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { && Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
refreshChildren(); refreshChildren();
} }
} }
}; };
@ -74,6 +78,78 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.DATA_SOURCE_ADDED), pcl);
} }
private DataSource getDs(List<DataSource> dataSources, int idx) {
return dataSources.get(idx % dataSources.size());
}
private OwnerHostTree getTree(SleuthkitCase tskCase) throws TskCoreException {
List<DataSource> 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<Object> getNodes(OwnerHostTree tree) {
List<Owner> owners = (tree == null) ? null : tree.getOwners();
List<Host> hosts = (tree == null) ? null : tree.getHosts();
List<DataSource> dataSources = (tree == null) ? null : tree.getDataSources();
if (CollectionUtils.isNotEmpty(owners)) {
List<Object> 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<Object> 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<DataSource> 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.
* *
@ -82,28 +158,11 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
*/ */
@Override @Override
protected boolean createKeys(List<Object> list) { protected boolean createKeys(List<Object> list) {
try { try {
SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase();
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
List<DataSource> dataSources = tskCase.getDataSources(); list.addAll(getNodes(getTree(tskCase)));
Collections.sort(dataSources, new Comparator<DataSource>() {
@Override
public int compare(DataSource dataS1, DataSource dataS2) {
String dataS1Name = dataS1.getName().toLowerCase();
String dataS2Name = dataS2.getName().toLowerCase();
return dataS1Name.compareTo(dataS2Name);
}
});
List<DataSourceGrouping> keys = new ArrayList<>();
dataSources.forEach((datasource) -> {
keys.add(new DataSourceGrouping(datasource));
});
list.addAll(keys);
list.add(new Reports()); list.add(new Reports());
} else { } else {
List<AutopsyVisitableItem> keys = new ArrayList<>(Arrays.asList( List<AutopsyVisitableItem> keys = new ArrayList<>(Arrays.asList(
@ -115,7 +174,6 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
list.addAll(keys); list.addAll(keys);
} }
} catch (TskCoreException tskCoreException) { } catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error getting datas sources list from the database.", tskCoreException); logger.log(Level.SEVERE, "Error getting datas sources list from the database.", tskCoreException);
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
@ -138,17 +196,191 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
return ((SleuthkitVisitableItem) key).accept(new CreateSleuthkitNodeVisitor()); return ((SleuthkitVisitableItem) key).accept(new CreateSleuthkitNodeVisitor());
} else if (key instanceof AutopsyVisitableItem) { } else if (key instanceof AutopsyVisitableItem) {
return ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor()); return ((AutopsyVisitableItem) key).accept(new RootContentChildren.CreateAutopsyNodeVisitor());
} } else {
else {
logger.log(Level.SEVERE, "Unknown key type ", key.getClass().getName()); logger.log(Level.SEVERE, "Unknown key type ", key.getClass().getName());
return null; return null;
} }
} }
/** /**
* Refresh the children * Refresh the children
*/ */
public void refreshChildren() { public void refreshChildren() {
refresh(true); refresh(true);
}
public static class Owner {
private final String name;
private final List<Host> hosts;
private final List<DataSource> dataSources;
public Owner(String name, List<Host> hosts, List<DataSource> dataSources) {
this.name = name;
this.hosts = hosts;
this.dataSources = dataSources;
}
public String getName() {
return name;
}
public List<Host> getHosts() {
return hosts;
}
public List<DataSource> getDataSources() {
return dataSources;
}
}
public static class Host {
private final String name;
private final List<DataSource> dataSources;
public Host(String name, List<DataSource> dataSources) {
this.name = name;
this.dataSources = (dataSources == null) ? Collections.emptyList() : Collections.unmodifiableList(new ArrayList<DataSource>(dataSources));
}
public List<DataSource> 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<DataSource> dataSources) {
return new HostNode(null, dataSources);
}
private static RootContentChildren getChildren(List<DataSource> dataSources) {
Stream<DataSourceGroupingNode> 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<DataSource> 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> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public String getItemType() {
return getClass().getName();
}
}
private static class OwnerHostTree {
private final List<Owner> owners;
private final List<Host> hosts;
private final List<DataSource> dataSources;
public OwnerHostTree(List<Owner> owners, List<Host> hosts, List<DataSource> dataSources) {
this.owners = owners;
this.hosts = hosts;
this.dataSources = dataSources;
}
public List<Owner> getOwners() {
return owners;
}
public List<Host> getHosts() {
return hosts;
}
public List<DataSource> getDataSources() {
return dataSources;
}
}
@Messages({
"OwnerNode_unknownHostNode_title=Unknown Owner"
})
static class OwnerNode extends DisplayableItemNode {
public static OwnerNode getUnknownOwner(List<Host> hosts, List<DataSource> dataSources) {
return new OwnerNode(null, hosts, dataSources);
}
private static RootContentChildren getChildren(List<Host> hosts, List<DataSource> dataSources) {
List<HostNode> 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<Host> hosts, List<DataSource> 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> T accept(DisplayableItemNodeVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public String getItemType() {
return getClass().getName();
}
} }
} }

View File

@ -193,6 +193,10 @@ public interface DisplayableItemNodeVisitor<T> {
*/ */
T visit(AttachmentNode node); T visit(AttachmentNode node);
T visit(AutopsyTreeChildFactory.OwnerNode aThis);
T visit(AutopsyTreeChildFactory.HostNode aThis);
/** /**
* Visitor with an implementable default behavior for all types. Override * Visitor with an implementable default behavior for all types. Override
* specific visit types to not use the default behavior. * specific visit types to not use the default behavior.
@ -540,5 +544,14 @@ public interface DisplayableItemNodeVisitor<T> {
return defaultVisit(node); return defaultVisit(node);
} }
@Override
public T visit(AutopsyTreeChildFactory.HostNode node) {
return defaultVisit(node);
}
@Override
public T visit(AutopsyTreeChildFactory.OwnerNode node) {
return defaultVisit(node);
}
} }
} }