From 1c2730c93d060e6b0197f715c743a0f7051e8f79 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 8 Apr 2021 15:17:58 -0400 Subject: [PATCH] Added background task to fetch realm data for OsAccountNode --- .../autopsy/datamodel/ContentNodeVisitor.java | 9 + .../autopsy/datamodel/OsAccounts.java | 167 +++++++++++++++--- 2 files changed, 149 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java b/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java index af7eb3a249..4d9f3e0870 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ContentNodeVisitor.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.datamodel; +import org.sleuthkit.autopsy.datamodel.OsAccounts.OsAccountNode; + /** * Visitor Pattern interface that goes over Content nodes in the data source * area of the tree. @@ -50,6 +52,8 @@ interface ContentNodeVisitor { T visit(BlackboardArtifactNode bban); + T visit(OsAccountNode bban); + /** * Visitor with an implementable default behavior for all types. Override @@ -122,5 +126,10 @@ interface ContentNodeVisitor { public T visit(BlackboardArtifactNode bban) { return defaultVisit(bban); } + + @Override + public T visit(OsAccountNode bban) { + return defaultVisit(bban); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java index 4b2588c6bb..9a9026b91c 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/OsAccounts.java @@ -18,8 +18,10 @@ */ package org.sleuthkit.autopsy.datamodel; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -27,20 +29,30 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.logging.Level; import javax.swing.Action; +import org.apache.commons.lang3.tuple.Pair; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.nodes.Sheet; +import org.openide.util.Exceptions; 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.events.OsAccountChangedEvent; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; 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.OsAccount; +import org.sleuthkit.datamodel.OsAccountRealm; import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.Tag; import org.sleuthkit.datamodel.TskCoreException; 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 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 String REALM_DATA_AVAILABLE_EVENT = "REALM_DATA_AVAILABLE_EVENT"; private SleuthkitCase skCase; private final long filteringDSObjId; @@ -114,9 +127,9 @@ public final class OsAccounts implements AutopsyVisitableItem { @Override public void propertyChange(PropertyChangeEvent evt) { 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())) { - refresh(true); + refresh(true); } 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 if (evt.getNewValue() == null) { @@ -126,22 +139,22 @@ public final class OsAccounts implements AutopsyVisitableItem { } } }; - + @Override protected void addNotify() { Case.addEventTypeSubscriber(EnumSet.of(Case.Events.OS_ACCOUNT_ADDED, Case.Events.OS_ACCOUNT_REMOVED), listener); Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); } - + @Override protected void removeNotify() { Case.removeEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_ADDED), listener); Case.removeEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), listener); } - + @Override protected boolean createKeys(List list) { - if(skCase != null) { + if (skCase != null) { try { if (filteringDSObjId == 0) { list.addAll(skCase.getOsAccountManager().getOsAccounts()); @@ -166,35 +179,52 @@ public final class OsAccounts implements AutopsyVisitableItem { /** * An OsAccount leaf Node. */ - public static final class OsAccountNode extends DisplayableItemNode { + public static final class OsAccountNode extends AbstractContentNode { private OsAccount account; - + private final PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - if(((OsAccountChangedEvent)evt).getOsAccount().getId() == account.getId()) { - // Update the account node to the new one - account = ((OsAccountChangedEvent)evt).getOsAccount(); - updateSheet(); + if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNT_CHANGED.name())) { + if (((OsAccountChangedEvent) evt).getOsAccount().getId() == account.getId()) { + // Update the account node to the new one + 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 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. * * @param account Node object. */ OsAccountNode(OsAccount account) { - super(Children.LEAF, Lookups.fixed(account)); + super(account); this.account = account; setName(account.getName()); setDisplayName(account.getName()); setIconBaseWithExtension(ICON_PATH); - - Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), listener); + + Case.addEventTypeSubscriber(Collections.singleton(Case.Events.OS_ACCOUNT_CHANGED), weakListener); } @Override @@ -211,7 +241,16 @@ public final class OsAccounts implements AutopsyVisitableItem { public String getItemType() { return getClass().getName(); } - + + /** + * Returns the OsAccount associated with this node. + * + * @return + */ + OsAccount getOsAccount() { + return account; + } + @Messages({ "OsAccounts_accountNameProperty_name=Name", "OsAccounts_accountNameProperty_displayName=Name", @@ -226,13 +265,13 @@ public final class OsAccounts implements AutopsyVisitableItem { "OsAccounts_loginNameProperty_displayName=Login Name", "OsAccounts_loginNameProperty_desc=Os Account login name" }) - + /** - * Refreshes this node's property sheet. - */ - void updateSheet() { - this.setSheet(createSheet()); - } + * Refreshes this node's property sheet. + */ + void updateSheet() { + this.setSheet(createSheet()); + } @Override protected Sheet createSheet() { @@ -256,9 +295,8 @@ public final class OsAccounts implements AutopsyVisitableItem { Bundle.OsAccounts_loginNameProperty_desc(), optional.isPresent() ? optional.get() : "")); - // TODO - load realm on background thread + // Fill with empty string, fetch on background task. String realmName = ""; - //String realmName = account.getRealm().getRealmNames().isEmpty() ? "" : account.getRealm().getRealmNames().get(0); propertiesSet.put(new NodeProperty<>( Bundle.OsAccounts_accountRealmNameProperty_name(), Bundle.OsAccounts_accountRealmNameProperty_displayName(), @@ -275,16 +313,91 @@ public final class OsAccounts implements AutopsyVisitableItem { Bundle.OsAccounts_createdTimeProperty_desc(), timeDisplayStr)); + backgroundTasksPool.submit(new GetOsAccountRealmTask(new WeakReference<>(this), weakListener)); + return sheet; } - + @Override public Action[] getActions(boolean popup) { List actionsList = new ArrayList<>(); actionsList.addAll(Arrays.asList(super.getActions(popup))); actionsList.addAll(DataModelActionsFactory.getActions(account)); - + return actionsList.toArray(new Action[actionsList.size()]); } + + @Override + protected List 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 getScorePropertyAndDescription(List tags) { + return null; + } + + @Override + protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { + return DataResultViewerTable.HasCommentStatus.NO_COMMENT; + } + + @Override + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) { + return null; + } + + @Override + public T accept(ContentNodeVisitor visitor) { + return visitor.visit(this); + } + + /** + * Task for grabbing the osAccount realm. + */ + static class GetOsAccountRealmTask implements Runnable { + + private final WeakReference weakNodeRef; + private final PropertyChangeListener listener; + + /** + * Construct a new task. + * + * @param weakContentRef + * @param listener + */ + GetOsAccountRealmTask(WeakReference 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); + } + } + } } }