Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.datamodel;
20
21import java.beans.PropertyChangeEvent;
22import java.beans.PropertyChangeListener;
23import java.lang.ref.WeakReference;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.EnumSet;
28import java.util.List;
29import java.util.Optional;
30import java.util.logging.Level;
31import java.util.stream.Collectors;
32import javax.swing.Action;
33import org.apache.commons.lang3.tuple.Pair;
34import javax.swing.SwingUtilities;
35import org.apache.commons.lang3.StringUtils;
36import org.openide.nodes.ChildFactory;
37import org.openide.nodes.Children;
38import org.openide.nodes.Node;
39import org.openide.nodes.Sheet;
40import org.openide.util.NbBundle;
41import org.openide.util.NbBundle.Messages;
42import org.openide.util.WeakListeners;
43import org.sleuthkit.autopsy.casemodule.Case;
44import org.sleuthkit.autopsy.casemodule.events.OsAccountsUpdatedEvent;
45import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbUtil;
46import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
47import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
48import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
49import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
50import org.sleuthkit.autopsy.core.UserPreferences;
51import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
52import org.sleuthkit.autopsy.coreutils.Logger;
53import org.sleuthkit.autopsy.coreutils.TimeZoneUtils;
54import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
55import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.VALUE_LOADING;
56import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
57import org.sleuthkit.autopsy.events.AutopsyEvent;
58import org.sleuthkit.datamodel.Host;
59import org.sleuthkit.datamodel.OsAccount;
60import org.sleuthkit.datamodel.OsAccountRealm;
61import org.sleuthkit.datamodel.SleuthkitCase;
62import org.sleuthkit.datamodel.Tag;
63import org.sleuthkit.datamodel.TskCoreException;
64
68public 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
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
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 void removeEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:757
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition Case.java:712
static boolean commentExistsOnAttributes(List< CorrelationAttributeInstance > attributes)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static String getFormattedTime(long epochTime)
static List< Action > getActions(File file, boolean isArtifactSource)
DataResultViewerTable.HasCommentStatus getCommentProperty(List< Tag > tags, List< CorrelationAttributeInstance > attributes)
Pair< Long, String > getCountPropertyAndDescription(CorrelationAttributeInstance attributeInstance, String defaultDescription)
static final String OS_ACCOUNT_DATA_AVAILABLE_EVENT
OsAccounts(SleuthkitCase skCase, long objId)
Long getCountCasesWithOtherInstances(CorrelationAttributeInstance instance)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.