Autopsy  4.18.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
EventsModel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2014-2019 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.timeline;
20 
21 import com.google.common.cache.CacheBuilder;
22 import com.google.common.cache.LoadingCache;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.eventbus.EventBus;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.concurrent.ExecutionException;
32 import java.util.concurrent.TimeUnit;
33 import java.util.logging.Level;
34 import javafx.beans.InvalidationListener;
35 import javafx.beans.property.ReadOnlyObjectProperty;
36 import javafx.beans.property.ReadOnlyObjectWrapper;
37 import javafx.collections.FXCollections;
38 import javafx.collections.ObservableMap;
39 import static org.apache.commons.collections4.CollectionUtils.emptyIfNull;
40 import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
41 import org.joda.time.DateTimeZone;
42 import org.joda.time.Interval;
43 import org.openide.util.NbBundle;
62 import org.sleuthkit.datamodel.AbstractFile;
63 import org.sleuthkit.datamodel.BlackboardArtifact;
64 import org.sleuthkit.datamodel.BlackboardArtifactTag;
65 import org.sleuthkit.datamodel.Content;
66 import org.sleuthkit.datamodel.ContentTag;
67 import org.sleuthkit.datamodel.DataSource;
68 import org.sleuthkit.datamodel.SleuthkitCase;
69 import org.sleuthkit.datamodel.TimelineManager;
70 import org.sleuthkit.datamodel.TskCoreException;
71 import org.sleuthkit.datamodel.TimelineEvent;
72 import org.sleuthkit.datamodel.TimelineEventType;
73 import org.sleuthkit.datamodel.TimelineFilter;
74 import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter;
75 import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter;
76 import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter;
77 import org.sleuthkit.datamodel.TimelineFilter.FileTypesFilter;
78 import org.sleuthkit.datamodel.TimelineFilter.HashHitsFilter;
79 import org.sleuthkit.datamodel.TimelineFilter.HideKnownFilter;
80 import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
81 import org.sleuthkit.datamodel.TimelineFilter.TagsFilter;
82 import org.sleuthkit.datamodel.TimelineFilter.TextFilter;
83 import org.sleuthkit.datamodel.TimelineLevelOfDetail;
84 
96 public final class EventsModel {
97 
98  private static final Logger logger = Logger.getLogger(EventsModel.class.getName());
99  private final EventBus eventbus = new EventBus("EventsModel_EventBus"); //NON-NLS
100  private final Case currentCase;
101  private final TimelineManager caseDbEventManager;
102 
103  /*
104  * User-specified parameters for the model exposed as JFX properties. These
105  * parameters apply across all of the views of the model and are set using
106  * GUI elements such the event filters panel.
107  *
108  * IMPORTANT: Note that the parameters are exposed both as a set and
109  * individually.
110  */
111  private final ReadOnlyObjectWrapper<EventsModelParams> modelParamsProperty = new ReadOnlyObjectWrapper<>();
112  private final ReadOnlyObjectWrapper<RootFilterState> filterStateProperty = new ReadOnlyObjectWrapper<>();
113  private final ReadOnlyObjectWrapper<Interval> timeRangeProperty = new ReadOnlyObjectWrapper<>();
114  private final ReadOnlyObjectWrapper<TimelineEventType.HierarchyLevel> eventTypesHierarchyLevelProperty = new ReadOnlyObjectWrapper<>(TimelineEventType.HierarchyLevel.CATEGORY);
115  private final ReadOnlyObjectWrapper<TimelineLevelOfDetail> timelineLODProperty = new ReadOnlyObjectWrapper<>(TimelineLevelOfDetail.LOW);
116 
117  /*
118  * Caches of model data from the case database.
119  */
120  private final ObservableMap<Long, String> datasourceIDsToNamesMap = FXCollections.observableHashMap();
121  private final LoadingCache<Object, Long> maxEventTimeCache;
122  private final LoadingCache<Object, Long> minEventTimeCache;
123  private final LoadingCache<Long, TimelineEvent> idsToEventsCache;
124  private final LoadingCache<EventsModelParams, Map<TimelineEventType, Long>> eventCountsCache;
125 
134  private static DataSourceFilter newDataSourceFilter(Map.Entry<Long, String> dataSourceEntry) {
135  return new DataSourceFilter(dataSourceEntry.getValue(), dataSourceEntry.getKey());
136  }
137 
147  public EventsModel(Case currentCase, ReadOnlyObjectProperty<EventsModelParams> modelParams) throws TskCoreException {
148  this.currentCase = currentCase;
149  this.caseDbEventManager = currentCase.getSleuthkitCase().getTimelineManager();
150 
151  /*
152  * Set up the caches of model data from the case database. Note that the
153  * build() method calls specify the methods used to create default cache
154  * entries when a call to get() would otherwise return a cache miss.
155  */
157  idsToEventsCache = CacheBuilder.newBuilder()
158  .maximumSize(5000L)
159  .expireAfterAccess(10, TimeUnit.MINUTES)
160  .build(new CacheLoaderImpl<>(caseDbEventManager::getEventById));
161  eventCountsCache = CacheBuilder.newBuilder()
162  .maximumSize(1000L)
163  .expireAfterAccess(10, TimeUnit.MINUTES)
164  .build(new CacheLoaderImpl<>(this::countEventsByType));
165  maxEventTimeCache = CacheBuilder.newBuilder()
166  .build(new CacheLoaderImpl<>(ignored -> caseDbEventManager.getMaxEventTime()));
167  minEventTimeCache = CacheBuilder.newBuilder()
168  .build(new CacheLoaderImpl<>(ignored -> caseDbEventManager.getMinEventTime()));
169 
170  /*
171  * Add a listener to the data sources cache that adds a data source
172  * filter to the event filter state model parameter when a data source
173  * is added to the cache.
174  */
175  InvalidationListener dataSourcesMapListener = observable -> {
176  RootFilterState rootFilter = filterStateProperty.getReadOnlyProperty().get();
177  addDataSourceFilters(rootFilter);
178  filterStateProperty.set(rootFilter.copyOf());
179  };
180  datasourceIDsToNamesMap.addListener(dataSourcesMapListener);
181 
182  /*
183  * Initialize the events filter state model parameter with the default
184  * events filter.
185  */
186  filterStateProperty.set(getDefaultEventFilterState());
187 
188  /*
189  * Add a listener to the model parameters property that updates the
190  * properties that expose the individual model parameters when they are
191  * changed through the model parameters property.
192  */
193  modelParamsProperty.addListener(observable -> {
194  final EventsModelParams params = modelParamsProperty.get();
195  if (params != null) {
196  synchronized (EventsModel.this) {
198  filterStateProperty.set(params.getEventFilterState());
199  timeRangeProperty.set(params.getTimeRange());
200  timelineLODProperty.set(params.getTimelineLOD());
201  }
202  }
203  });
204 
205  modelParamsProperty.bind(modelParams);
206  }
207 
212  synchronized private void populateDataSourcesCache() throws TskCoreException {
213  SleuthkitCase skCase = currentCase.getSleuthkitCase();
214  for (DataSource ds : skCase.getDataSources()) {
215  datasourceIDsToNamesMap.putIfAbsent(ds.getId(), ds.getName());
216  }
217  }
218 
225  synchronized void addDataSourceFilters(RootFilterState rootFilterState) {
226  datasourceIDsToNamesMap.entrySet().forEach(entry -> rootFilterState.getDataSourcesFilterState().addSubFilterState(new SqlFilterState<>(newDataSourceFilter(entry))));
227  }
228 
242  private Map<TimelineEventType, Long> countEventsByType(EventsModelParams modelParams) throws TskCoreException {
243  if (modelParams.getTimeRange() == null) {
244  return Collections.emptyMap();
245  } else {
246  return caseDbEventManager.countEventsByType(modelParams.getTimeRange().getStartMillis() / 1000,
247  modelParams.getTimeRange().getEndMillis() / 1000,
248  modelParams.getEventFilterState().getActiveFilter(),
249  modelParams.getEventTypesHierarchyLevel());
250  }
251  }
252 
258  public TimelineManager getEventManager() {
259  return caseDbEventManager;
260  }
261 
267  public SleuthkitCase getSleuthkitCase() {
268  return currentCase.getSleuthkitCase();
269  }
270 
277  synchronized public ReadOnlyObjectProperty<EventsModelParams> modelParamsProperty() {
278  return modelParamsProperty.getReadOnlyProperty();
279  }
280 
286  @NbBundle.Messages({
287  "FilteredEventsModel.timeRangeProperty.errorTitle=Timeline",
288  "FilteredEventsModel.timeRangeProperty.errorMessage=Error getting spanning interval."})
289  synchronized public ReadOnlyObjectProperty<Interval> timeRangeProperty() {
290  if (timeRangeProperty.get() == null) {
291  try {
292  timeRangeProperty.set(EventsModel.this.getSpanningInterval());
293  } catch (TskCoreException timelineCacheException) {
294  MessageNotifyUtil.Notify.error(Bundle.FilteredEventsModel_timeRangeProperty_errorTitle(),
295  Bundle.FilteredEventsModel_timeRangeProperty_errorMessage());
296  logger.log(Level.SEVERE, "Error getting spanning interval.", timelineCacheException);
297  }
298  }
299  return timeRangeProperty.getReadOnlyProperty();
300  }
301 
308  synchronized public ReadOnlyObjectProperty<TimelineLevelOfDetail> descriptionLODProperty() {
309  return timelineLODProperty.getReadOnlyProperty();
310  }
311 
318  synchronized public ReadOnlyObjectProperty<RootFilterState> eventFilterProperty() {
319  return filterStateProperty.getReadOnlyProperty();
320  }
321 
328  synchronized public ReadOnlyObjectProperty<TimelineEventType.HierarchyLevel> eventTypesHierarchyLevelProperty() {
329  return eventTypesHierarchyLevelProperty.getReadOnlyProperty();
330  }
331 
337  synchronized public EventsModelParams getModelParams() {
338  return modelParamsProperty.get();
339  }
340 
346  synchronized public Interval getTimeRange() {
347  return getModelParams().getTimeRange();
348  }
349 
355  synchronized public TimelineLevelOfDetail getDescriptionLOD() {
356  return getModelParams().getTimelineLOD();
357  }
358 
364  synchronized public RootFilterState getEventFilterState() {
366  }
367 
373  synchronized public TimelineEventType.HierarchyLevel getEventTypeZoom() {
375  }
376 
385  /*
386  * Construct data source filters for all of the data sources in the data
387  * sources cache.
388  */
389  DataSourcesFilter dataSourcesFilter = new DataSourcesFilter();
390  datasourceIDsToNamesMap.entrySet().forEach(dataSourceEntry
391  -> dataSourcesFilter.addSubFilter(newDataSourceFilter(dataSourceEntry)));
392 
393  /*
394  * Make the rest of the event filters and wrap all of the filters with
395  * filter state objects for the GUI.
396  */
397  RootFilterState rootFilterState = new RootFilterState(new RootFilter(
398  new HideKnownFilter(),
399  new TagsFilter(),
400  new HashHitsFilter(),
401  new TextFilter(),
402  new EventTypeFilter(TimelineEventType.ROOT_EVENT_TYPE),
403  dataSourcesFilter,
405  Collections.emptySet()));
406 
407  return rootFilterState;
408  }
409 
420  public TimelineEvent getEventById(Long eventID) throws TskCoreException {
421  try {
422  return idsToEventsCache.get(eventID);
423  } catch (ExecutionException ex) {
424  throw new TskCoreException("Error getting cached event from ID", ex);
425  }
426  }
427 
438  public Set<TimelineEvent> getEventsById(Collection<Long> eventIDs) throws TskCoreException {
439  Set<TimelineEvent> events = new HashSet<>();
440  for (Long id : eventIDs) {
441  events.add(getEventById(id));
442  }
443  return events;
444  }
445 
458  public List<Long> getEventIDs(Interval timeRange, FilterState<? extends TimelineFilter> filterState) throws TskCoreException {
459  final Interval overlap;
460  RootFilter intersection;
461  synchronized (this) {
462  overlap = EventsModel.this.getSpanningInterval().overlap(timeRange);
463  intersection = getEventFilterState().intersect(filterState).getActiveFilter();
464  }
465  return caseDbEventManager.getEventIDs(overlap, intersection);
466  }
467 
481  public Set<Long> getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts) throws TskCoreException {
482  return caseDbEventManager.getEventIDsForContent(file, includeDerivedArtifacts);
483  }
484 
495  public List<Long> getEventIDsForArtifact(BlackboardArtifact artifact) throws TskCoreException {
496  return caseDbEventManager.getEventIDsForArtifact(artifact);
497  }
498 
509  public Map<TimelineEventType, Long> getEventCounts(Interval timeRange) throws TskCoreException {
510  final RootFilterState filter;
511  final TimelineEventType.HierarchyLevel typeZoom;
512  synchronized (this) {
513  filter = getEventFilterState();
514  typeZoom = getEventTypeZoom();
515  }
516  try {
517  return eventCountsCache.get(new EventsModelParams(timeRange, typeZoom, filter, null));
518  } catch (ExecutionException executionException) {
519  throw new TskCoreException("Error getting cached event counts.`1", executionException);
520  }
521  }
522 
534  public Interval getSpanningInterval(DateTimeZone timeZone) throws TskCoreException {
535  return caseDbEventManager.getSpanningInterval(modelParamsProperty().get().getTimeRange(), getEventFilterState().getActiveFilter(), timeZone);
536  }
537 
546  public Interval getSpanningInterval() throws TskCoreException {
547  return new Interval(getMinEventTime() * 1000, 1000 + getMaxEventTime() * 1000);
548  }
549 
560  public Interval getSpanningInterval(Collection<Long> eventIDs) throws TskCoreException {
561  return caseDbEventManager.getSpanningInterval(eventIDs);
562  }
563 
573  public Long getMinEventTime() throws TskCoreException {
574  try {
575  return minEventTimeCache.get("min"); // NON-NLS
576  } catch (ExecutionException ex) {
577  throw new TskCoreException("Error getting cached min time.", ex);
578  }
579  }
580 
590  public Long getMaxEventTime() throws TskCoreException {
591  try {
592  return maxEventTimeCache.get("max"); // NON-NLS
593  } catch (ExecutionException ex) {
594  throw new TskCoreException("Error getting cached max time.", ex);
595  }
596  }
597 
609  synchronized public boolean handleContentTagAdded(ContentTagAddedEvent evt) throws TskCoreException {
610  ContentTag contentTag = evt.getAddedTag();
611  Content content = contentTag.getContent();
612  Set<Long> updatedEventIDs = caseDbEventManager.updateEventsForContentTagAdded(content);
613  if (isNotEmpty(updatedEventIDs)) {
614  invalidateCaches(updatedEventIDs);
615  }
616  return postTagsAdded(updatedEventIDs);
617  }
618 
630  synchronized public boolean handleArtifactTagAdded(BlackBoardArtifactTagAddedEvent evt) throws TskCoreException {
631  BlackboardArtifactTag artifactTag = evt.getAddedTag();
632  BlackboardArtifact artifact = artifactTag.getArtifact();
633  Set<Long> updatedEventIDs = caseDbEventManager.updateEventsForArtifactTagAdded(artifact);
634  if (isNotEmpty(updatedEventIDs)) {
635  invalidateCaches(updatedEventIDs);
636  }
637  return postTagsAdded(updatedEventIDs);
638  }
639 
651  synchronized public boolean handleContentTagDeleted(ContentTagDeletedEvent evt) throws TskCoreException {
652  DeletedContentTagInfo deletedTagInfo = evt.getDeletedTagInfo();
653  Content content = currentCase.getSleuthkitCase().getContentById(deletedTagInfo.getContentID());
654  Set<Long> updatedEventIDs = caseDbEventManager.updateEventsForContentTagDeleted(content);
655  if (isNotEmpty(updatedEventIDs)) {
656  invalidateCaches(updatedEventIDs);
657  }
658  return postTagsDeleted(updatedEventIDs);
659  }
660 
667  synchronized void handleDataSourceAdded() throws TskCoreException {
669  invalidateCaches(null);
670  }
671 
683  synchronized public boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt) throws TskCoreException {
684  DeletedBlackboardArtifactTagInfo deletedTagInfo = evt.getDeletedTagInfo();
685  BlackboardArtifact artifact = currentCase.getSleuthkitCase().getBlackboardArtifact(deletedTagInfo.getArtifactID());
686  Set<Long> updatedEventIDs = caseDbEventManager.updateEventsForArtifactTagDeleted(artifact);
687  if (isNotEmpty(updatedEventIDs)) {
688  invalidateCaches(updatedEventIDs);
689  }
690  return postTagsDeleted(updatedEventIDs);
691  }
692 
702  private boolean postTagsAdded(Set<Long> updatedEventIDs) {
703  boolean tagsUpdated = !updatedEventIDs.isEmpty();
704  if (tagsUpdated) {
705  eventbus.post(new TagsAddedEvent(updatedEventIDs));
706  }
707  return tagsUpdated;
708  }
709 
719  private boolean postTagsDeleted(Set<Long> updatedEventIDs) {
720  boolean tagsUpdated = !updatedEventIDs.isEmpty();
721  if (tagsUpdated) {
722  eventbus.post(new TagsDeletedEvent(updatedEventIDs));
723  }
724  return tagsUpdated;
725  }
726 
733  synchronized public void registerForEvents(Object subscriber) {
734  eventbus.register(subscriber);
735  }
736 
742  synchronized public void unRegisterForEvents(Object subscriber) {
743  eventbus.unregister(subscriber);
744  }
745 
749  public void postRefreshRequest() {
750  eventbus.post(new RefreshRequestedEvent());
751  }
752 
758  public ImmutableList<TimelineEventType> getEventTypes() {
759  return caseDbEventManager.getEventTypes();
760  }
761 
773  synchronized public Set<Long> updateEventsForHashSetHits(Collection<BlackboardArtifact> hashSetHitArtifacts) throws TskCoreException {
774  Set<Long> updatedEventIDs = new HashSet<>();
775  for (BlackboardArtifact artifact : hashSetHitArtifacts) {
776  Content content = currentCase.getSleuthkitCase().getContentById(artifact.getObjectID());
777  updatedEventIDs.addAll(caseDbEventManager.updateEventsForHashSetHit(content));
778  }
779  if (isNotEmpty(updatedEventIDs)) {
780  invalidateCaches(updatedEventIDs);
781  }
782  return updatedEventIDs;
783  }
784 
795  public synchronized void invalidateCaches(Collection<Long> updatedEventIDs) throws TskCoreException {
796  minEventTimeCache.invalidateAll();
797  maxEventTimeCache.invalidateAll();
798  idsToEventsCache.invalidateAll(emptyIfNull(updatedEventIDs));
799  eventCountsCache.invalidateAll();
800  eventbus.post(new CacheInvalidatedEvent());
801  }
802 
807  public static class CacheInvalidatedEvent {
808 
810  }
811  }
812 
813 }
synchronized boolean handleArtifactTagAdded(BlackBoardArtifactTagAddedEvent evt)
Set< Long > getEventIDsForFile(AbstractFile file, boolean includeDerivedArtifacts)
synchronized Set< Long > updateEventsForHashSetHits(Collection< BlackboardArtifact > hashSetHitArtifacts)
ImmutableList< TimelineEventType > getEventTypes()
Set< TimelineEvent > getEventsById(Collection< Long > eventIDs)
synchronized void unRegisterForEvents(Object subscriber)
synchronized void invalidateCaches(Collection< Long > updatedEventIDs)
synchronized RootFilterState getEventFilterState()
void addSubFilterState(FilterState< ?extends SubFilterType > newSubFilterState)
Interval getSpanningInterval(Collection< Long > eventIDs)
final LoadingCache< Long, TimelineEvent > idsToEventsCache
static DataSourceFilter newDataSourceFilter(Map.Entry< Long, String > dataSourceEntry)
synchronized boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt)
synchronized EventsModelParams getModelParams()
List< Long > getEventIDs(Interval timeRange, FilterState<?extends TimelineFilter > filterState)
Interval getSpanningInterval(DateTimeZone timeZone)
synchronized ReadOnlyObjectProperty< EventsModelParams > modelParamsProperty()
final LoadingCache< EventsModelParams, Map< TimelineEventType, Long > > eventCountsCache
final LoadingCache< Object, Long > maxEventTimeCache
EventsModel(Case currentCase, ReadOnlyObjectProperty< EventsModelParams > modelParams)
boolean postTagsAdded(Set< Long > updatedEventIDs)
TimelineEventType.HierarchyLevel getEventTypesHierarchyLevel()
final ReadOnlyObjectWrapper< TimelineLevelOfDetail > timelineLODProperty
final ReadOnlyObjectWrapper< RootFilterState > filterStateProperty
synchronized TimelineEventType.HierarchyLevel getEventTypeZoom()
synchronized RootFilterState getDefaultEventFilterState()
boolean postTagsDeleted(Set< Long > updatedEventIDs)
final ObservableMap< Long, String > datasourceIDsToNamesMap
synchronized ReadOnlyObjectProperty< Interval > timeRangeProperty()
static FileTypesFilter createDefaultFileTypesFilter()
synchronized void registerForEvents(Object subscriber)
synchronized ReadOnlyObjectProperty< TimelineEventType.HierarchyLevel > eventTypesHierarchyLevelProperty()
RootFilterState intersect(FilterState< ?extends TimelineFilter > other)
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
Map< TimelineEventType, Long > countEventsByType(EventsModelParams modelParams)
synchronized boolean handleContentTagDeleted(ContentTagDeletedEvent evt)
synchronized TimelineLevelOfDetail getDescriptionLOD()
synchronized boolean handleContentTagAdded(ContentTagAddedEvent evt)
synchronized ReadOnlyObjectProperty< TimelineLevelOfDetail > descriptionLODProperty()
CompoundFilterState< DataSourceFilter, DataSourcesFilter > getDataSourcesFilterState()
TimelineEvent getEventById(Long eventID)
List< Long > getEventIDsForArtifact(BlackboardArtifact artifact)
Map< TimelineEventType, Long > getEventCounts(Interval timeRange)
synchronized ReadOnlyObjectProperty< RootFilterState > eventFilterProperty()
final LoadingCache< Object, Long > minEventTimeCache

Copyright © 2012-2021 Basis Technology. Generated on: Thu Jul 8 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.