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,15 +44,14 @@ 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
* *
*/ */
public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Object> { public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Object> {
private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName()); private static final Logger logger = Logger.getLogger(AutopsyTreeChildFactory.class.getName());
/** /**
* Listener for handling DATA_SOURCE_ADDED events. * Listener for handling DATA_SOURCE_ADDED events.
*/ */
@ -55,13 +59,13 @@ 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();
} }
} }
}; };
@Override @Override
protected void addNotify() { protected void addNotify() {
super.addNotify(); super.addNotify();
@ -73,37 +77,92 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
super.removeNotify(); super.removeNotify();
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.
* *
* @param list list of keys created * @param list list of keys created
* @return true, indicating that the key list is complete * @return true, indicating that the key list is complete
*/ */
@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) {
@ -123,32 +181,206 @@ public final class AutopsyTreeChildFactory extends ChildFactory.Detachable<Objec
} }
return true; return true;
} }
/** /**
* Creates nodes for the top level Key * Creates nodes for the top level Key
* *
* @param key * @param key
* *
* @return Node for the key, null if key is unknown. * @return Node for the key, null if key is unknown.
*/ */
@Override @Override
protected Node createNodeForKey(Object key) { protected Node createNodeForKey(Object key) {
if (key instanceof SleuthkitVisitableItem) { if (key instanceof SleuthkitVisitableItem) {
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

@ -59,7 +59,7 @@ public interface DisplayableItemNodeVisitor<T> {
T visit(ImageNode in); T visit(ImageNode in);
T visit(VolumeNode vn); T visit(VolumeNode vn);
T visit(PoolNode pn); T visit(PoolNode pn);
T visit(SlackFileNode sfn); T visit(SlackFileNode sfn);
@ -192,7 +192,11 @@ public interface DisplayableItemNodeVisitor<T> {
* Attachments * Attachments
*/ */
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.
@ -264,7 +268,7 @@ public interface DisplayableItemNodeVisitor<T> {
public T visit(ImageNode in) { public T visit(ImageNode in) {
return defaultVisit(in); return defaultVisit(in);
} }
@Override @Override
public T visit(PoolNode pn) { public T visit(PoolNode pn) {
return defaultVisit(pn); return defaultVisit(pn);
@ -534,11 +538,20 @@ public interface DisplayableItemNodeVisitor<T> {
public T visit(Accounts.DefaultAccountTypeNode node) { public T visit(Accounts.DefaultAccountTypeNode node) {
return defaultVisit(node); return defaultVisit(node);
} }
@Override @Override
public T visit(AttachmentNode node) { public T visit(AttachmentNode node) {
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);
}
} }
} }