Autopsy  4.14.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;
61 import org.sleuthkit.datamodel.AbstractFile;
62 import org.sleuthkit.datamodel.BlackboardArtifact;
63 import org.sleuthkit.datamodel.BlackboardArtifactTag;
64 import org.sleuthkit.datamodel.Content;
65 import org.sleuthkit.datamodel.ContentTag;
66 import org.sleuthkit.datamodel.DataSource;
67 import org.sleuthkit.datamodel.SleuthkitCase;
68 import org.sleuthkit.datamodel.TimelineManager;
69 import org.sleuthkit.datamodel.TskCoreException;
70 import org.sleuthkit.datamodel.TimelineEvent;
71 import org.sleuthkit.datamodel.TimelineEventType;
72 import org.sleuthkit.datamodel.TimelineFilter;
73 import org.sleuthkit.datamodel.TimelineFilter.DataSourceFilter;
74 import org.sleuthkit.datamodel.TimelineFilter.DataSourcesFilter;
75 import org.sleuthkit.datamodel.TimelineFilter.EventTypeFilter;
76 import org.sleuthkit.datamodel.TimelineFilter.FileTypesFilter;
77 import org.sleuthkit.datamodel.TimelineFilter.HashHitsFilter;
78 import org.sleuthkit.datamodel.TimelineFilter.HideKnownFilter;
79 import org.sleuthkit.datamodel.TimelineFilter.RootFilter;
80 import org.sleuthkit.datamodel.TimelineFilter.TagsFilter;
81 import org.sleuthkit.datamodel.TimelineFilter.TextFilter;
82 import org.sleuthkit.datamodel.TimelineLevelOfDetail;
83 
95 public final class EventsModel {
96 
97  private static final Logger logger = Logger.getLogger(EventsModel.class.getName());
98  private final EventBus eventbus = new EventBus("EventsModel_EventBus"); //NON-NLS
99  private final Case currentCase;
100  private final TimelineManager caseDbEventManager;
101 
102  /*
103  * User-specified parameters for the model exposed as JFX properties. These
104  * parameters apply across all of the views of the model and are set using
105  * GUI elements such the event filters panel.
106  *
107  * IMPORTANT: Note that the parameters are exposed both as a set and
108  * individually.
109  */
110  private final ReadOnlyObjectWrapper<EventsModelParams> modelParamsProperty = new ReadOnlyObjectWrapper<>();
111  private final ReadOnlyObjectWrapper<RootFilterState> filterStateProperty = new ReadOnlyObjectWrapper<>();
112  private final ReadOnlyObjectWrapper<Interval> timeRangeProperty = new ReadOnlyObjectWrapper<>();
113  private final ReadOnlyObjectWrapper<TimelineEventType.HierarchyLevel> eventTypesHierarchyLevelProperty = new ReadOnlyObjectWrapper<>(TimelineEventType.HierarchyLevel.CATEGORY);
114  private final ReadOnlyObjectWrapper<TimelineLevelOfDetail> timelineLODProperty = new ReadOnlyObjectWrapper<>(TimelineLevelOfDetail.LOW);
115 
116  /*
117  * Caches of model data from the case database.
118  */
119  private final ObservableMap<Long, String> datasourceIDsToNamesMap = FXCollections.observableHashMap();
120  private final LoadingCache<Object, Long> maxEventTimeCache;
121  private final LoadingCache<Object, Long> minEventTimeCache;
122  private final LoadingCache<Long, TimelineEvent> idsToEventsCache;
123  private final LoadingCache<EventsModelParams, Map<TimelineEventType, Long>> eventCountsCache;
124 
133  private static DataSourceFilter newDataSourceFilter(Map.Entry<Long, String> dataSourceEntry) {
134  return new DataSourceFilter(dataSourceEntry.getValue(), dataSourceEntry.getKey());
135  }
136 
146  public EventsModel(Case currentCase, ReadOnlyObjectProperty<EventsModelParams> modelParams) throws TskCoreException {
147  this.currentCase = currentCase;
148  this.caseDbEventManager = currentCase.getSleuthkitCase().getTimelineManager();
149 
150  /*
151  * Set up the caches of model data from the case database. Note that the
152  * build() method calls specify the methods used to create default cache
153  * entries when a call to get() would otherwise return a cache miss.
154  */
156  idsToEventsCache = CacheBuilder.newBuilder()
157  .maximumSize(5000L)
158  .expireAfterAccess(10, TimeUnit.MINUTES)
159  .build(new CacheLoaderImpl<>(caseDbEventManager::getEventById));
160  eventCountsCache = CacheBuilder.newBuilder()
161  .maximumSize(1000L)
162  .expireAfterAccess(10, TimeUnit.MINUTES)
163  .build(new CacheLoaderImpl<>(this::countEventsByType));
164  maxEventTimeCache = CacheBuilder.newBuilder()
165  .build(new CacheLoaderImpl<>(ignored -> caseDbEventManager.getMaxEventTime()));
166  minEventTimeCache = CacheBuilder.newBuilder()
167  .build(new CacheLoaderImpl<>(ignored -> caseDbEventManager.getMinEventTime()));
168 
169  /*
170  * Add a listener to the data sources cache that adds a data source
171  * filter to the event filter state model parameter when a data source
172  * is added to the cache.
173  */
174  InvalidationListener dataSourcesMapListener = observable -> {
175  RootFilterState rootFilter = filterStateProperty.getReadOnlyProperty().get();
176  addDataSourceFilters(rootFilter);
177  filterStateProperty.set(rootFilter.copyOf());
178  };
179  datasourceIDsToNamesMap.addListener(dataSourcesMapListener);
180 
181  /*
182  * Initialize the events filter state model parameter with the default
183  * events filter.
184  */
185  filterStateProperty.set(getDefaultEventFilterState());
186 
187  /*
188  * Add a listener to the model parameters property that updates the
189  * properties that expose the individual model parameters when they are
190  * changed through the model parameters property.
191  */
192  modelParamsProperty.addListener(observable -> {
193  final EventsModelParams params = modelParamsProperty.get();
194  if (params != null) {
195  synchronized (EventsModel.this) {
197  filterStateProperty.set(params.getEventFilterState());
198  timeRangeProperty.set(params.getTimeRange());
199  timelineLODProperty.set(params.getTimelineLOD());
200  }
201  }
202  });
203 
204  modelParamsProperty.bind(modelParams);
205  }
206 
211  synchronized private void populateDataSourcesCache() throws TskCoreException {
212  SleuthkitCase skCase = currentCase.getSleuthkitCase();
213  for (DataSource ds : skCase.getDataSources()) {
214  datasourceIDsToNamesMap.putIfAbsent(ds.getId(), ds.getName());
215  }
216  }
217 
224  synchronized void addDataSourceFilters(RootFilterState rootFilterState) {
225  DataSourcesFilter dataSourcesFilter = rootFilterState.getDataSourcesFilterState().getFilter();
226  datasourceIDsToNamesMap.entrySet().forEach(entry -> dataSourcesFilter.addSubFilter(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 
672  synchronized public boolean handleArtifactTagDeleted(BlackBoardArtifactTagDeletedEvent evt) throws TskCoreException {
673  DeletedBlackboardArtifactTagInfo deletedTagInfo = evt.getDeletedTagInfo();
674  BlackboardArtifact artifact = currentCase.getSleuthkitCase().getBlackboardArtifact(deletedTagInfo.getArtifactID());
675  Set<Long> updatedEventIDs = caseDbEventManager.updateEventsForArtifactTagDeleted(artifact);
676  if (isNotEmpty(updatedEventIDs)) {
677  invalidateCaches(updatedEventIDs);
678  }
679  return postTagsDeleted(updatedEventIDs);
680  }
681 
691  private boolean postTagsAdded(Set<Long> updatedEventIDs) {
692  boolean tagsUpdated = !updatedEventIDs.isEmpty();
693  if (tagsUpdated) {
694  eventbus.post(new TagsAddedEvent(updatedEventIDs));
695  }
696  return tagsUpdated;
697  }
698 
708  private boolean postTagsDeleted(Set<Long> updatedEventIDs) {
709  boolean tagsUpdated = !updatedEventIDs.isEmpty();
710  if (tagsUpdated) {
711  eventbus.post(new TagsDeletedEvent(updatedEventIDs));
712  }
713  return tagsUpdated;
714  }
715 
722  synchronized public void registerForEvents(Object subscriber) {
723  eventbus.register(subscriber);
724  }
725 
731  synchronized public void unRegisterForEvents(Object subscriber) {
732  eventbus.unregister(subscriber);
733  }
734 
738  public void postRefreshRequest() {
739  eventbus.post(new RefreshRequestedEvent());
740  }
741 
747  public ImmutableList<TimelineEventType> getEventTypes() {
748  return caseDbEventManager.getEventTypes();
749  }
750 
762  synchronized public Set<Long> updateEventsForHashSetHits(Collection<BlackboardArtifact> hashSetHitArtifacts) throws TskCoreException {
763  Set<Long> updatedEventIDs = new HashSet<>();
764  for (BlackboardArtifact artifact : hashSetHitArtifacts) {
765  Content content = currentCase.getSleuthkitCase().getContentById(artifact.getObjectID());
766  updatedEventIDs.addAll(caseDbEventManager.updateEventsForHashSetHit(content));
767  }
768  if (isNotEmpty(updatedEventIDs)) {
769  invalidateCaches(updatedEventIDs);
770  }
771  return updatedEventIDs;
772  }
773 
784  public synchronized void invalidateCaches(Collection<Long> updatedEventIDs) throws TskCoreException {
786  minEventTimeCache.invalidateAll();
787  maxEventTimeCache.invalidateAll();
788  idsToEventsCache.invalidateAll(emptyIfNull(updatedEventIDs));
789  eventCountsCache.invalidateAll();
790  eventbus.post(new CacheInvalidatedEvent());
791  }
792 
797  public static class CacheInvalidatedEvent {
798 
800  }
801  }
802 
803 }
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()
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-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.