Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
OsAccounts.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.datamodel;
20 
21 import java.beans.PropertyChangeEvent;
22 import java.beans.PropertyChangeListener;
23 import java.lang.ref.WeakReference;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.EnumSet;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.logging.Level;
31 import java.util.stream.Collectors;
32 import javax.swing.Action;
33 import org.apache.commons.lang3.tuple.Pair;
34 import javax.swing.SwingUtilities;
35 import org.apache.commons.lang3.StringUtils;
36 import org.openide.nodes.ChildFactory;
37 import org.openide.nodes.Children;
38 import org.openide.nodes.Node;
39 import org.openide.nodes.Sheet;
40 import org.openide.util.NbBundle;
41 import org.openide.util.NbBundle.Messages;
42 import org.openide.util.WeakListeners;
56 import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
58 import org.sleuthkit.datamodel.Host;
59 import org.sleuthkit.datamodel.OsAccount;
60 import org.sleuthkit.datamodel.OsAccountRealm;
61 import org.sleuthkit.datamodel.SleuthkitCase;
62 import org.sleuthkit.datamodel.Tag;
63 import org.sleuthkit.datamodel.TskCoreException;
64 
68 public final class OsAccounts implements AutopsyVisitableItem {
69 
70  private static final Logger logger = Logger.getLogger(OsAccounts.class.getName());
71  private static final String ICON_PATH = "org/sleuthkit/autopsy/images/os-account.png";
72  private static final String OS_ACCOUNT_DATA_AVAILABLE_EVENT = "OS_ACCOUNT_DATA_AVAILABLE_EVENT";
73 
74  private static final String LIST_NAME = Bundle.OsAccount_listNode_name();
75 
76  private SleuthkitCase skCase;
77  private final long filteringDSObjId;
78 
84  public static String getListName() {
85  return LIST_NAME;
86  }
87 
88  public OsAccounts(SleuthkitCase skCase) {
89  this(skCase, 0);
90  }
91 
92  public OsAccounts(SleuthkitCase skCase, long objId) {
93  this.skCase = skCase;
94  this.filteringDSObjId = objId;
95  }
96 
97  @Override
98  public <T> T accept(AutopsyItemVisitor<T> visitor) {
99  return visitor.visit(this);
100  }
101 
102  @Messages({
103  "OsAccount_listNode_name=OS Accounts"
104  })
108  public final class OsAccountListNode extends DisplayableItemNode {
109 
113  public OsAccountListNode() {
114  super(Children.create(new OsAccountNodeFactory(), true));
115  setName(LIST_NAME);
116  setDisplayName(LIST_NAME);
117  setIconBaseWithExtension("org/sleuthkit/autopsy/images/os-account.png");
118  }
119 
120  @Override
121  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
122  return visitor.visit(this);
123  }
124 
125  @Override
126  public boolean isLeafTypeNode() {
127  return true;
128  }
129 
130  @Override
131  public String getItemType() {
132  return getClass().getName();
133  }
134  }
135 
140  private final class OsAccountNodeFactory extends ChildFactory.Detachable<OsAccount> {
141 
142  private final PropertyChangeListener listener = new PropertyChangeListener() {
143  @Override
144  public void propertyChange(PropertyChangeEvent evt) {
145  String eventType = evt.getPropertyName();
146  if (eventType.equals(Case.Events.OS_ACCOUNTS_ADDED.toString())
147  || eventType.equals(Case.Events.OS_ACCOUNTS_DELETED.toString())) {
148  refresh(true);
149  } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
150  // case was closed. Remove listeners so that we don't get called with a stale case handle
151  if (evt.getNewValue() == null) {
152  removeNotify();
153  skCase = null;
154  }
155  }
156  }
157  };
158 
159  private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(listener, null);
160 
161  @Override
162  protected void finalize() throws Throwable {
163  super.finalize();
166  }
167 
168  @Override
169  protected void addNotify() {
172  }
173 
174  @Override
175  protected boolean createKeys(List<OsAccount> list) {
176  if (skCase != null) {
177  try {
178  if (filteringDSObjId == 0) {
179  list.addAll(skCase.getOsAccountManager().getOsAccounts());
180  } else {
181  list.addAll(skCase.getOsAccountManager().getOsAccountsByDataSourceObjId(filteringDSObjId));
182  }
183  } catch (TskCoreException ex) {
184  logger.log(Level.SEVERE, "Unable to retrieve list of OsAccounts for case", ex);
185  return false;
186  }
187  }
188  return true;
189  }
190 
191  @Override
192  protected Node createNodeForKey(OsAccount key) {
193  return new OsAccountNode(key);
194  }
195  }
196 
200  public static final class OsAccountNode extends AbstractContentNode<OsAccount> {
201 
202  private OsAccount account;
203 
204  @Messages({
205  "OsAccounts_accountNameProperty_name=Name",
206  "OsAccounts_accountNameProperty_displayName=Name",
207  "OsAccounts_accountNameProperty_desc=Os Account name",
208  "OsAccounts_accountRealmNameProperty_name=RealmName",
209  "OsAccounts_accountRealmNameProperty_displayName=Realm Name",
210  "OsAccounts_accountRealmNameProperty_desc=OS Account Realm Name",
211  "OsAccounts_accountHostNameProperty_name=HostName",
212  "OsAccounts_accountHostNameProperty_displayName=Host",
213  "OsAccounts_accountHostNameProperty_desc=OS Account Host Name",
214  "OsAccounts_accountScopeNameProperty_name=ScopeName",
215  "OsAccounts_accountScopeNameProperty_displayName=Scope",
216  "OsAccounts_accountScopeNameProperty_desc=OS Account Scope Name",
217  "OsAccounts_createdTimeProperty_name=creationTime",
218  "OsAccounts_createdTimeProperty_displayName=Creation Time",
219  "OsAccounts_createdTimeProperty_desc=OS Account Creation Time",
220  "OsAccounts_loginNameProperty_name=loginName",
221  "OsAccounts_loginNameProperty_displayName=Login Name",
222  "OsAccounts_loginNameProperty_desc=OS Account login name",
223  "OsAccounts.createSheet.score.name=S",
224  "OsAccounts.createSheet.score.displayName=S",
225  "OsAccounts.createSheet.count.name=O",
226  "OsAccounts.createSheet.count.displayName=O",
227  "OsAccounts.createSheet.comment.name=C",
228  "OsAccounts.createSheet.comment.displayName=C"
229  })
230  private final PropertyChangeListener listener = new PropertyChangeListener() {
231  @Override
232  public void propertyChange(PropertyChangeEvent evt) {
233  if (evt.getPropertyName().equals(Case.Events.OS_ACCOUNTS_UPDATED.name())) {
235  for (OsAccount acct : updateEvent.getOsAccounts()) {
236  if (acct.getId() == account.getId()) {
237  account = acct;
238  updateSheet();
239  break;
240  }
241  }
242  } else if (evt.getPropertyName().equals(OS_ACCOUNT_DATA_AVAILABLE_EVENT)
243  && evt.getNewValue() instanceof AsynchOsAcctData
244  && ((AsynchOsAcctData) evt.getNewValue()).getOsAccountId() == account.getId()) {
245 
246  List<NodeProperty<?>> propertiesToUpdate = new ArrayList<>();
247 
248  AsynchOsAcctData osAcctData = (AsynchOsAcctData) evt.getNewValue();
249 
250  List<String> realmNames = osAcctData.getOsAcctRealm().getRealmNames();
251  if (!realmNames.isEmpty()) {
252  String realmNamesStr = realmNames.stream()
253  .map(String::trim)
254  .distinct()
255  .sorted((a, b) -> a.compareToIgnoreCase(b))
256  .collect(Collectors.joining(", "));
257 
258  propertiesToUpdate.add(new NodeProperty<>(
259  Bundle.OsAccounts_accountRealmNameProperty_name(),
260  Bundle.OsAccounts_accountRealmNameProperty_displayName(),
261  Bundle.OsAccounts_accountRealmNameProperty_desc(),
262  realmNamesStr));
263  }
264 
265  String scopeName = osAcctData.getOsAcctRealm().getScope().getName();
266  if (StringUtils.isNotBlank(scopeName)) {
267  propertiesToUpdate.add(new NodeProperty<>(
268  Bundle.OsAccounts_accountScopeNameProperty_name(),
269  Bundle.OsAccounts_accountScopeNameProperty_displayName(),
270  Bundle.OsAccounts_accountScopeNameProperty_desc(),
271  scopeName));
272  }
273 
274  List<Host> hosts = osAcctData.getHosts();
275  if (!hosts.isEmpty()) {
276  String hostsString = hosts.stream()
277  .map(h -> h.getName().trim())
278  .distinct()
279  .sorted((a, b) -> a.compareToIgnoreCase(b))
280  .collect(Collectors.joining(", "));
281 
282  propertiesToUpdate.add(new NodeProperty<>(
283  Bundle.OsAccounts_accountHostNameProperty_name(),
284  Bundle.OsAccounts_accountHostNameProperty_displayName(),
285  Bundle.OsAccounts_accountHostNameProperty_desc(),
286  hostsString));
287  }
288  updateSheet(propertiesToUpdate.toArray(new NodeProperty<?>[propertiesToUpdate.size()]));
289  } else if (evt.getPropertyName().equals(NodeSpecificEvents.SCO_AVAILABLE.toString()) && !UserPreferences.getHideSCOColumns()) {
290  SCOData scoData = (SCOData) evt.getNewValue();
291  if (scoData.getScoreAndDescription() != null) {
292  updateSheet(new NodeProperty<>(
293  Bundle.OsAccounts_createSheet_score_name(),
294  Bundle.OsAccounts_createSheet_score_displayName(),
295  scoData.getScoreAndDescription().getRight(),
296  scoData.getScoreAndDescription().getLeft()));
297  }
298  if (scoData.getComment() != null) {
299  updateSheet(new NodeProperty<>(
300  Bundle.OsAccounts_createSheet_comment_name(),
301  Bundle.OsAccounts_createSheet_comment_displayName(),
302  NO_DESCR, scoData.getComment()));
303  }
304  if (scoData.getCountAndDescription() != null) {
305  updateSheet(new NodeProperty<>(
306  Bundle.OsAccounts_createSheet_count_name(),
307  Bundle.OsAccounts_createSheet_count_displayName(),
308  scoData.getCountAndDescription().getRight(),
309  scoData.getCountAndDescription().getLeft()));
310  }
311  }
312  }
313  };
314 
315  private final PropertyChangeListener weakListener = WeakListeners.propertyChange(listener, null);
316 
322  OsAccountNode(OsAccount account) {
323  super(account);
324  this.account = account;
325 
326  setName(account.getName());
327  setDisplayName(account.getName());
328  setIconBaseWithExtension(ICON_PATH);
329 
331  }
332 
333  @Override
334  public <T> T accept(DisplayableItemNodeVisitor<T> visitor) {
335  return visitor.visit(this);
336  }
337 
338  @Override
339  public boolean isLeafTypeNode() {
340  return true;
341  }
342 
343  @Override
344  public String getItemType() {
345  return getClass().getName();
346  }
347 
353  OsAccount getOsAccount() {
354  return account;
355  }
356 
360  void updateSheet() {
361  SwingUtilities.invokeLater(() -> {
362  this.setSheet(createSheet());
363  });
364  }
365 
366  @Override
367  protected Sheet createSheet() {
368  Sheet sheet = super.createSheet();
369  Sheet.Set propertiesSet = sheet.get(Sheet.PROPERTIES);
370  if (propertiesSet == null) {
371  propertiesSet = Sheet.createPropertiesSet();
372  sheet.put(propertiesSet);
373  }
374  propertiesSet.put(new NodeProperty<>(
375  Bundle.OsAccounts_accountNameProperty_name(),
376  Bundle.OsAccounts_accountNameProperty_displayName(),
377  Bundle.OsAccounts_accountNameProperty_desc(),
378  account.getName() != null ? account.getName() : ""));
379  addSCOColumns(propertiesSet);
380  Optional<String> optional = account.getLoginName();
381  propertiesSet.put(new NodeProperty<>(
382  Bundle.OsAccounts_loginNameProperty_name(),
383  Bundle.OsAccounts_loginNameProperty_displayName(),
384  Bundle.OsAccounts_loginNameProperty_desc(),
385  optional.isPresent() ? optional.get() : ""));
386 
387  // Fill with empty string, fetch on background task.
388  propertiesSet.put(new NodeProperty<>(
389  Bundle.OsAccounts_accountHostNameProperty_name(),
390  Bundle.OsAccounts_accountHostNameProperty_displayName(),
391  Bundle.OsAccounts_accountHostNameProperty_desc(),
392  ""));
393 
394  propertiesSet.put(new NodeProperty<>(
395  Bundle.OsAccounts_accountScopeNameProperty_name(),
396  Bundle.OsAccounts_accountScopeNameProperty_displayName(),
397  Bundle.OsAccounts_accountScopeNameProperty_desc(),
398  ""));
399 
400  propertiesSet.put(new NodeProperty<>(
401  Bundle.OsAccounts_accountRealmNameProperty_name(),
402  Bundle.OsAccounts_accountRealmNameProperty_displayName(),
403  Bundle.OsAccounts_accountRealmNameProperty_desc(),
404  ""));
405 
406  Optional<Long> creationTimeValue = account.getCreationTime();
407  String timeDisplayStr
408  = creationTimeValue.isPresent() ? TimeZoneUtils.getFormattedTime(creationTimeValue.get()) : "";
409 
410  propertiesSet.put(new NodeProperty<>(
411  Bundle.OsAccounts_createdTimeProperty_name(),
412  Bundle.OsAccounts_createdTimeProperty_displayName(),
413  Bundle.OsAccounts_createdTimeProperty_desc(),
414  timeDisplayStr));
415 
416  backgroundTasksPool.submit(new GetOsAccountRealmTask(new WeakReference<>(this), weakListener));
417  return sheet;
418  }
419 
420  private void addSCOColumns(Sheet.Set sheetSet) {
422  /*
423  * Add S(core), C(omments), and O(ther occurences) columns to
424  * the sheet and start a background task to compute the value of
425  * these properties for the artifact represented by this node.
426  * The task will fire a PropertyChangeEvent when the computation
427  * is completed and this node's PropertyChangeListener will
428  * update the sheet.
429  */
430  sheetSet.put(new NodeProperty<>(
431  Bundle.OsAccounts_createSheet_score_name(),
432  Bundle.OsAccounts_createSheet_score_displayName(),
434  ""));
435  sheetSet.put(new NodeProperty<>(
436  Bundle.OsAccounts_createSheet_comment_name(),
437  Bundle.OsAccounts_createSheet_comment_displayName(),
439  ""));
441  sheetSet.put(new NodeProperty<>(
442  Bundle.OsAccounts_createSheet_count_name(),
443  Bundle.OsAccounts_createSheet_count_displayName(),
445  ""));
446  }
447  backgroundTasksPool.submit(new GetSCOTask(new WeakReference<>(this), weakListener));
448  }
449  }
450 
451  @Override
452  public Action[] getActions(boolean popup) {
453  List<Action> actionsList = new ArrayList<>();
454  actionsList.addAll(DataModelActionsFactory.getActions(account));
455  actionsList.add(null);
456  actionsList.addAll(Arrays.asList(super.getActions(popup)));
457  return actionsList.toArray(new Action[actionsList.size()]);
458  }
459 
460  @Override
461  protected List<Tag> getAllTagsFromDatabase() {
462  return new ArrayList<>();
463  }
464 
465  @Override
466  public <T> T accept(ContentNodeVisitor<T> visitor) {
467  return visitor.visit(this);
468  }
469 
473  static class GetOsAccountRealmTask implements Runnable {
474 
475  private final WeakReference<OsAccountNode> weakNodeRef;
476  private final PropertyChangeListener listener;
477 
484  GetOsAccountRealmTask(WeakReference<OsAccountNode> weakContentRef, PropertyChangeListener listener) {
485  this.weakNodeRef = weakContentRef;
486  this.listener = listener;
487  }
488 
489  @Override
490  public void run() {
491  OsAccountNode node = weakNodeRef.get();
492  if (node == null) {
493  return;
494  }
495 
496  try {
497  SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
498  OsAccount osAcct = node.getOsAccount();
499  long realmId = osAcct.getRealmId();
500  OsAccountRealm realm = skCase.getOsAccountRealmManager().getRealmByRealmId(realmId);
501 
502  List<Host> hosts = skCase.getOsAccountManager().getHosts(osAcct);
503 
504  AsynchOsAcctData evtData = new AsynchOsAcctData(osAcct.getId(), realm, hosts);
505 
506  if (listener != null && realm != null) {
507  listener.propertyChange(new PropertyChangeEvent(
508  AutopsyEvent.SourceType.LOCAL.toString(),
510  null, evtData));
511  }
512  } catch (TskCoreException ex) {
513  logger.log(Level.WARNING, "Error occurred getting realm information for Os Account Node from case db, for account: " + node.getOsAccount().getName(), ex);
514  }
515  }
516  }
517 
518  @NbBundle.Messages({
519  "OsAccounts.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
520  "# {0} - occurrenceCount",
521  "OsAccounts.createSheet.count.description=There were {0} datasource(s) found with occurrences of the OS Account correlation value"})
522  @Override
523 
524  protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attributeInstance, String defaultDescription) {
525  Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
526  String description = defaultDescription;
527  try {
528  //don't perform the query if there is no correlation value
529  if (attributeInstance != null && StringUtils.isNotBlank(attributeInstance.getCorrelationValue())) {
531  description = Bundle.OsAccounts_createSheet_count_description(count);
532  } else if (attributeInstance != null) {
533  description = Bundle.OsAccounts_createSheet_count_hashLookupNotRun_description();
534  }
535  } catch (CentralRepoException ex) {
536  logger.log(Level.SEVERE, String.format("Error getting count of data sources with %s correlation attribute %s", attributeInstance.getCorrelationType().getDisplayName(), attributeInstance.getCorrelationValue()), ex);
538  logger.log(Level.WARNING, String.format("Unable to normalize %s correlation attribute %s", attributeInstance.getCorrelationType().getDisplayName(), attributeInstance.getCorrelationValue()), ex);
539  }
540  return Pair.of(count, description);
541  }
542 
551  @Override
552  protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, List<CorrelationAttributeInstance> attributes) {
553  /*
554  * Has a tag with a comment been applied to the OsAccount or its
555  * source content?
556  */
558  for (Tag tag : tags) {
559  if (!StringUtils.isBlank(tag.getComment())) {
561  break;
562  }
563  }
564  /*
565  * Is there a comment in the CR for anything that matches the value
566  * and type of the specified attributes.
567  */
568  try {
572  } else {
574  }
575  }
576  } catch (CentralRepoException ex) {
577  logger.log(Level.SEVERE, "Attempted to Query CR for presence of comments in an OS Account node and was unable to perform query, comment column will only reflect caseDB", ex);
578  }
579  return status;
580  }
581 
586  private static class AsynchOsAcctData {
587 
588  private final long osAccountId;
589  private final OsAccountRealm osAcctRealm;
590  private final List<Host> hosts;
591 
599  AsynchOsAcctData(long osAccountId, OsAccountRealm osAcctRealm, List<Host> hosts) {
600  this.osAccountId = osAccountId;
601  this.osAcctRealm = osAcctRealm;
602  this.hosts = hosts;
603  }
604 
608  long getOsAccountId() {
609  return osAccountId;
610  }
611 
615  OsAccountRealm getOsAcctRealm() {
616  return osAcctRealm;
617  }
618 
622  List<Host> getHosts() {
623  return hosts;
624  }
625 
626  }
627  }
628 }
static final String OS_ACCOUNT_DATA_AVAILABLE_EVENT
Definition: OsAccounts.java:72
static List< Action > getActions(File file, boolean isArtifactSource)
OsAccounts(SleuthkitCase skCase, long objId)
Definition: OsAccounts.java:92
static String getFormattedTime(long epochTime)
static boolean commentExistsOnAttributes(List< CorrelationAttributeInstance > attributes)
Pair< Long, String > getCountPropertyAndDescription(CorrelationAttributeInstance attributeInstance, String defaultDescription)
Long getCountCasesWithOtherInstances(CorrelationAttributeInstance instance)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:704
DataResultViewerTable.HasCommentStatus getCommentProperty(List< Tag > tags, List< CorrelationAttributeInstance > attributes)
static void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:749

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.