Added background task to fetch realm data for OsAccountNode

This commit is contained in:
Kelly Kelly 2021-04-08 15:17:58 -04:00
parent d8aa4ab07a
commit 1c2730c93d
2 changed files with 149 additions and 27 deletions

View File

@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode;
/** /**
* Visitor Pattern interface that goes over Content nodes in the data source * Visitor Pattern interface that goes over Content nodes in the data source
* area of the tree. * area of the tree.
@ -50,6 +52,8 @@ interface ContentNodeVisitor<T> {
T visit(BlackboardArtifactNode bban); T visit(BlackboardArtifactNode bban);
T visit(OsAccountNode bban);
/** /**
* Visitor with an implementable default behavior for all types. Override * Visitor with an implementable default behavior for all types. Override
@ -122,5 +126,10 @@ interface ContentNodeVisitor<T> {
public T visit(BlackboardArtifactNode bban) { public T visit(BlackboardArtifactNode bban) {
return defaultVisit(bban); return defaultVisit(bban);
} }
@Override
public T visit(OsAccountNode bban) {
return defaultVisit(bban);
}
} }
} }

View File

@ -18,8 +18,10 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -27,20 +29,30 @@ import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.Action; import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups; import org.openide.util.WeakListeners;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent; import org.sleuthkit.autopsy.casemodule.events.OsAccountChangedEvent;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.Host; import org.sleuthkit.datamodel.Host;
import org.sleuthkit.datamodel.OsAccount; import org.sleuthkit.datamodel.OsAccount;
import org.sleuthkit.datamodel.OsAccountRealm;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskDataException;
@ -52,6 +64,7 @@ public final class OsAccounts implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(OsAccounts.class.getName()); private static final Logger logger = Logger.getLogger(OsAccounts.class.getName());
private static final String ICON_PATH = "org/sleuthkit/autopsy/images/os-account.png"; private static final String ICON_PATH = "org/sleuthkit/autopsy/images/os-account.png";
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
private static final String REALM_DATA_AVAILABLE_EVENT = "REALM_DATA_AVAILABLE_EVENT";
private SleuthkitCase skCase; private SleuthkitCase skCase;
private final long filteringDSObjId; private final long filteringDSObjId;
@ -114,9 +127,9 @@ public final class OsAccounts implements AutopsyVisitableItem {
@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.OS_ACCOUNT_ADDED.toString()) if (eventType.equals(Case.Events.OS_ACCOUNT_ADDED.toString())
|| eventType.equals(Case.Events.OS_ACCOUNT_REMOVED.toString())) { || eventType.equals(Case.Events.OS_ACCOUNT_REMOVED.toString())) {
refresh(true); refresh(true);
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
// case was closed. Remove listeners so that we don't get called with a stale case handle // case was closed. Remove listeners so that we don't get called with a stale case handle
if (evt.getNewValue() == null) { if (evt.getNewValue() == null) {
@ -126,22 +139,22 @@ public final class OsAccounts implements AutopsyVisitableItem {
} }
} }
}; };
@Override @Override
protected void addNotify() { protected void addNotify() {
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener);
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
} }
@Override @Override
protected void removeNotify() { protected void removeNotify() {
Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener); Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener);
Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener);
} }
@Override @Override
protected boolean createKeys(List<OsAccount> list) { protected boolean createKeys(List<OsAccount> list) {
if(skCase != null) { if (skCase != null) {
try { try {
if (filteringDSObjId == 0) { if (filteringDSObjId == 0) {
list.addAll(skCase.getOsAccountManager().getOsAccounts()); list.addAll(skCase.getOsAccountManager().getOsAccounts());
@ -166,35 +179,52 @@ public final class OsAccounts implements AutopsyVisitableItem {
/** /**
* An OsAccount leaf Node. * An OsAccount leaf Node.
*/ */
public static final class OsAccountNode extends DisplayableItemNode { public static final class OsAccountNode extends AbstractContentNode<OsAccount> {
private OsAccount account; private OsAccount account;
private final PropertyChangeListener listener = new PropertyChangeListener() { private final PropertyChangeListener listener = new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if(((OsAccountChangedEvent)evt).getOsAccount().getId() == account.getId()) { if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) {
// Update the account node to the new one if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) {
account = ((OsAccountChangedEvent)evt).getOsAccount(); // Update the account node to the new one
updateSheet(); account = ((OsAccountChangedEvent) evt).getOsAccount();
updateSheet();
}
} else if (evt.getPropertyName().equals(REALM_DATA_AVAILABLE_EVENT)) {
OsAccountRealm realm = (OsAccountRealm) evt.getNewValue();
// Currently only 0 or 1 names are supported, this will need
// to be modified if that changes.
List<String> realmNames = realm.getRealmNames();
if (!realmNames.isEmpty()) {
updateSheet(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(),
Bundle.OsAccounts_accountRealmNameProperty_desc(),
""));
}
} }
} }
}; };
private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
/** /**
* Constructs a new OsAccountNode. * Constructs a new OsAccountNode.
* *
* @param account Node object. * @param account Node object.
*/ */
OsAccountNode(OsAccount account) { OsAccountNode(OsAccount account) {
super(Children.LEAF, Lookups.fixed(account)); super(account);
this.account = account; this.account = account;
setName(account.getName()); setName(account.getName());
setDisplayName(account.getName()); setDisplayName(account.getName());
setIconBaseWithExtension(ICON_PATH); setIconBaseWithExtension(ICON_PATH);
Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener); Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener);
} }
@Override @Override
@ -211,7 +241,16 @@ public final class OsAccounts implements AutopsyVisitableItem {
public String getItemType() { public String getItemType() {
return getClass().getName(); return getClass().getName();
} }
/**
* Returns the OsAccount associated with this node.
*
* @return
*/
OsAccount getOsAccount() {
return account;
}
@Messages({ @Messages({
"OsAccounts_accountNameProperty_name=Name", "OsAccounts_accountNameProperty_name=Name",
"OsAccounts_accountNameProperty_displayName=Name", "OsAccounts_accountNameProperty_displayName=Name",
@ -226,13 +265,13 @@ public final class OsAccounts implements AutopsyVisitableItem {
"OsAccounts_loginNameProperty_displayName=Login Name", "OsAccounts_loginNameProperty_displayName=Login Name",
"OsAccounts_loginNameProperty_desc=Os Account login name" "OsAccounts_loginNameProperty_desc=Os Account login name"
}) })
/** /**
* Refreshes this node's property sheet. * Refreshes this node's property sheet.
*/ */
void updateSheet() { void updateSheet() {
this.setSheet(createSheet()); this.setSheet(createSheet());
} }
@Override @Override
protected Sheet createSheet() { protected Sheet createSheet() {
@ -256,9 +295,8 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_loginNameProperty_desc(), Bundle.OsAccounts_loginNameProperty_desc(),
optional.isPresent() ? optional.get() : "")); optional.isPresent() ? optional.get() : ""));
// TODO - load realm on background thread // Fill with empty string, fetch on background task.
String realmName = ""; String realmName = "";
//String realmName = account.getRealm().getRealmNames().isEmpty() ? "" : account.getRealm().getRealmNames().get(0);
propertiesSet.put(new NodeProperty<>( propertiesSet.put(new NodeProperty<>(
Bundle.OsAccounts_accountRealmNameProperty_name(), Bundle.OsAccounts_accountRealmNameProperty_name(),
Bundle.OsAccounts_accountRealmNameProperty_displayName(), Bundle.OsAccounts_accountRealmNameProperty_displayName(),
@ -275,16 +313,91 @@ public final class OsAccounts implements AutopsyVisitableItem {
Bundle.OsAccounts_createdTimeProperty_desc(), Bundle.OsAccounts_createdTimeProperty_desc(),
timeDisplayStr)); timeDisplayStr));
backgroundTasksPool.submit(new GetOsAccountRealmTask(new WeakReference<>(this), weakListener));
return sheet; return sheet;
} }
@Override @Override
public Action[] getActions(boolean popup) { public Action[] getActions(boolean popup) {
List<Action> actionsList = new ArrayList<>(); List<Action> actionsList = new ArrayList<>();
actionsList.addAll(Arrays.asList(super.getActions(popup))); actionsList.addAll(Arrays.asList(super.getActions(popup)));
actionsList.addAll(DataModelActionsFactory.getActions(account)); actionsList.addAll(DataModelActionsFactory.getActions(account));
return actionsList.toArray(new Action[actionsList.size()]); return actionsList.toArray(new Action[actionsList.size()]);
} }
@Override
protected List<Tag> getAllTagsFromDatabase() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return null;
}
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return null;
}
@Override
public <T> T accept(ContentNodeVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Task for grabbing the osAccount realm.
*/
static class GetOsAccountRealmTask implements Runnable {
private final WeakReference<OsAccountNode> weakNodeRef;
private final PropertyChangeListener listener;
/**
* Construct a new task.
*
* @param weakContentRef
* @param listener
*/
GetOsAccountRealmTask(WeakReference<OsAccountNode> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
@Override
public void run() {
OsAccountNode node = weakNodeRef.get();
if (node == null) {
return;
}
try {
long realmId = node.getOsAccount().getRealmId();
OsAccountRealm realm = Case.getCurrentCase().getSleuthkitCase().getOsAccountRealmManager().getRealmByRealmId(realmId);
if (listener != null && realm != null) {
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
REALM_DATA_AVAILABLE_EVENT,
null, realm));
}
} catch (TskCoreException ex) {
Exceptions.printStackTrace(ex);
}
}
}
} }
} }