Merge pull request #4914 from kellykelly3/1268-dynamic-account-types

1268 dynamic account types
This commit is contained in:
Richard Cordovano 2019-06-20 09:55:56 -04:00 committed by GitHub
commit a70a910fab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 50 deletions

View File

@ -66,7 +66,7 @@
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/> <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="5" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
</Component> </Component>
@ -95,7 +95,7 @@
</Properties> </Properties>
<Constraints> <Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription"> <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/> <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint> </Constraint>
</Constraints> </Constraints>
@ -464,7 +464,7 @@
<Container class="javax.swing.JPanel" name="accountTypeListPane"> <Container class="javax.swing.JPanel" name="accountTypeListPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"> <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/> <Property name="axis" type="int" value="3"/>
</Layout> </Layout>
</Container> </Container>
</SubComponents> </SubComponents>

View File

@ -53,6 +53,7 @@ import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestJobEvent.COMPLETED;
import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Account;
@ -63,6 +64,7 @@ import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter;
import org.sleuthkit.datamodel.CommunicationsFilter.MostRecentFilter; import org.sleuthkit.datamodel.CommunicationsFilter.MostRecentFilter;
import org.sleuthkit.datamodel.CommunicationsManager;
import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.DataSource;
import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG;
import static org.sleuthkit.datamodel.Relationship.Type.CONTACT; import static org.sleuthkit.datamodel.Relationship.Type.CONTACT;
@ -96,9 +98,10 @@ final public class FiltersPanel extends JPanel {
* Listens to ingest events to enable refresh button * Listens to ingest events to enable refresh button
*/ */
private final PropertyChangeListener ingestListener; private final PropertyChangeListener ingestListener;
private final PropertyChangeListener ingestJobListener;
/** /**
* Flag that indicates the UI is not up-sto-date with respect to the case DB * Flag that indicates the UI is not up-to-date with respect to the case DB
* and it should be refreshed (by reapplying the filters). * and it should be refreshed (by reapplying the filters).
*/ */
private boolean needsRefresh; private boolean needsRefresh;
@ -123,6 +126,11 @@ final public class FiltersPanel extends JPanel {
@NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"}) @NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"})
public FiltersPanel() { public FiltersPanel() {
initComponents(); initComponents();
CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(Account.Type.DEVICE, true);
accountTypeMap.put(Account.Type.DEVICE, panel.getCheckBox());
accountTypeListPane.add(panel);
deviceRequiredLabel.setVisible(false); deviceRequiredLabel.setVisible(false);
accountTypeRequiredLabel.setVisible(false); accountTypeRequiredLabel.setVisible(false);
startDatePicker.setDate(LocalDate.now().minusWeeks(3)); startDatePicker.setDate(LocalDate.now().minusWeeks(3));
@ -155,18 +163,30 @@ final public class FiltersPanel extends JPanel {
// Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits // Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits
ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue();
if (null != eventData if (null != eventData
&& eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() && (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID()
&& eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()
updateFilters(false); || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()))
{
updateFilters(true);
needsRefresh = true;
validateFilters();
}
}
};
this.ingestJobListener = pce -> {
String eventType = pce.getPropertyName();
if (eventType.equals(COMPLETED.toString()) &&
updateFilters(true)) {
needsRefresh = true; needsRefresh = true;
validateFilters(); validateFilters();
}
} }
}; };
applyFiltersButton.addActionListener(e -> applyFilters()); applyFiltersButton.addActionListener(e -> applyFilters());
refreshButton.addActionListener(e -> applyFilters()); refreshButton.addActionListener(e -> applyFilters());
} }
/** /**
@ -219,19 +239,26 @@ final public class FiltersPanel extends JPanel {
/** /**
* Updates the filter widgets to reflect he data sources/types in the case. * Updates the filter widgets to reflect he data sources/types in the case.
*/ */
private void updateFilters(boolean initialState) { private boolean updateFilters(boolean initialState) {
updateAccountTypeFilter(); boolean newAccountType = updateAccountTypeFilter(initialState);
updateDeviceFilter(initialState); boolean newDeviceFilter = updateDeviceFilter(initialState);
// both or either are true, return true;
return newAccountType || newDeviceFilter;
} }
@Override @Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
IngestManager.getInstance().addIngestModuleEventListener(ingestListener); IngestManager.getInstance().addIngestModuleEventListener(ingestListener);
IngestManager.getInstance().addIngestJobEventListener(ingestJobListener);
Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> {
//clear the device filter widget when the case changes. //clear the device filter widget when the case changes.
devicesMap.clear(); devicesMap.clear();
devicesListPane.removeAll(); devicesListPane.removeAll();
accountTypeMap.clear();
accountTypeListPane.removeAll();
}); });
} }
@ -239,64 +266,107 @@ final public class FiltersPanel extends JPanel {
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); IngestManager.getInstance().removeIngestModuleEventListener(ingestListener);
IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener);
} }
/** /**
* Populate the Account Types filter widgets * Populate the Account Types filter widgets
*
* @param selected the initial value for the account type checkbox
*
* @return True, if a new accountType was found
*/ */
private void updateAccountTypeFilter() { private boolean updateAccountTypeFilter(boolean selected) {
boolean newOneFound = false;
//TODO: something like this commented code could be used to show only try {
//the account types that are found: final CommunicationsManager communicationsManager = Case.getCurrentCaseThrows().getSleuthkitCase().getCommunicationsManager();
//final CommunicationsManager communicationsManager = Case.getCurrentOpenCase().getSleuthkitCase().getCommunicationsManager(); List<Account.Type> accountTypesInUse = communicationsManager.getAccountTypesInUse();
//List<Account.Type> accountTypesInUse = communicationsManager.getAccountTypesInUse();
//accountTypesInUSe.forEach(...) for (Account.Type type : accountTypesInUse) {
Account.Type.PREDEFINED_ACCOUNT_TYPES.forEach(type -> {
if (type.equals(Account.Type.CREDIT_CARD)) { if (!accountTypeMap.containsKey(type) && !type.equals(Account.Type.CREDIT_CARD)) {
//don't show a check box for credit cards CheckBoxIconPanel panel = createAccoutTypeCheckBoxPanel(type, selected);
} else { accountTypeMap.put(type, panel.getCheckBox());
accountTypeMap.computeIfAbsent(type, t -> {
CheckBoxIconPanel panel = new CheckBoxIconPanel(
type.getDisplayName(),
new ImageIcon(FiltersPanel.class.getResource(Utils.getIconFilePath(type))));
panel.setSelected(true);
panel.addItemListener(validationListener);
accountTypeListPane.add(panel); accountTypeListPane.add(panel);
if (t.equals(Account.Type.DEVICE)) {
//Deveice type filter is enabled based on whether we are in table or graph view. newOneFound = true;
panel.setEnabled(deviceAccountTypeEnabled); }
}
return panel.getCheckBox();
});
} }
});
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update to update Account Types Filter", ex);
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "A case is required to update the account types filter.", ex);
}
if (newOneFound) {
accountTypeListPane.revalidate();
}
return newOneFound;
}
/**
* Helper function to create a new instance of the CheckBoxIconPanel base on
* the Account.Type and initalState (check box state).
*
* @param type Account.Type to display on the panel
* @param initalState initial check box state
*
* @return instance of the CheckBoxIconPanel
*/
private CheckBoxIconPanel createAccoutTypeCheckBoxPanel(Account.Type type, boolean initalState) {
CheckBoxIconPanel panel = new CheckBoxIconPanel(
type.getDisplayName(),
new ImageIcon(FiltersPanel.class.getResource(Utils.getIconFilePath(type))));
panel.setSelected(initalState);
panel.addItemListener(validationListener);
if (type.equals(Account.Type.DEVICE)) {
//Deveice type filter is enabled based on whether we are in table or graph view.
panel.setEnabled(deviceAccountTypeEnabled);
}
return panel;
} }
/** /**
* Populate the devices filter widgets * Populate the devices filter widgets
* *
* @param initialState * @param selected Sets the initial state of device check box
*
* @return true if a new device was found
*/ */
private void updateDeviceFilter(boolean initialState) { private boolean updateDeviceFilter(boolean selected) {
boolean newOneFound = false;
try { try {
final SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase(); final SleuthkitCase sleuthkitCase = Case.getCurrentCaseThrows().getSleuthkitCase();
for (DataSource dataSource : sleuthkitCase.getDataSources()) { for (DataSource dataSource : sleuthkitCase.getDataSources()) {
String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName();
//store the device id in the map, but display a datasource name in the UI. if(devicesMap.containsKey(dataSource.getDeviceId())) {
devicesMap.computeIfAbsent(dataSource.getDeviceId(), ds -> { continue;
final JCheckBox jCheckBox = new JCheckBox(dsName, initialState); }
jCheckBox.addItemListener(validationListener);
devicesListPane.add(jCheckBox); final JCheckBox jCheckBox = new JCheckBox(dsName, selected);
return jCheckBox; jCheckBox.addItemListener(validationListener);
}); devicesListPane.add(jCheckBox);
devicesMap.put(dataSource.getDeviceId(), jCheckBox);
newOneFound = true;
} }
} catch (NoCurrentCaseException ex) { } catch (NoCurrentCaseException ex) {
logger.log(Level.INFO, "Filter update cancelled. Case is closed."); logger.log(Level.INFO, "Filter update cancelled. Case is closed.");
} catch (TskCoreException tskCoreException) { } catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException); logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException);
} }
if(newOneFound) {
devicesListPane.revalidate();
}
return newOneFound;
} }
/** /**
@ -319,7 +389,7 @@ final public class FiltersPanel extends JPanel {
} }
/** /**
* Sets the state of the device filter checkboxes * Sets the state of the device filter check boxes
* *
* @param deviceFilter Selected devices * @param deviceFilter Selected devices
*/ */
@ -366,6 +436,12 @@ final public class FiltersPanel extends JPanel {
endDatePicker.setEnabled(state.isEnabled()); endDatePicker.setEnabled(state.isEnabled());
} }
/**
* Sets the state of the most recent UI controls based on the current values
* in MostRecentFilter.
*
* @param filter The MostRecentFilter state to be set
*/
private void setMostRecentFilter(MostRecentFilter filter) { private void setMostRecentFilter(MostRecentFilter filter) {
int limit = filter.getLimit(); int limit = filter.getLimit();
if(limit > 0) { if(limit > 0) {
@ -424,6 +500,7 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.gridx = 1; gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0; gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5);
topPane.add(applyFiltersButton, gridBagConstraints); topPane.add(applyFiltersButton, gridBagConstraints);
needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N
@ -697,7 +774,7 @@ final public class FiltersPanel extends JPanel {
accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200)); accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200));
accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.Y_AXIS)); accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.PAGE_AXIS));
accountTypesScrollPane.setViewportView(accountTypeListPane); accountTypesScrollPane.setViewportView(accountTypeListPane);
gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints = new java.awt.GridBagConstraints();
@ -740,6 +817,7 @@ final public class FiltersPanel extends JPanel {
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0; gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0; gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0);
add(scrollPane, gridBagConstraints); add(scrollPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
@ -808,6 +886,11 @@ final public class FiltersPanel extends JPanel {
endCheckBox.isSelected() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0); endCheckBox.isSelected() ? endDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0);
} }
/**
* Get a MostRecentFilter that based on the current state of the ui controls.
*
* @return A new instance of MostRecentFilter
*/
private MostRecentFilter getMostRecentFilter() { private MostRecentFilter getMostRecentFilter() {
String value = (String)limitComboBox.getSelectedItem(); String value = (String)limitComboBox.getSelectedItem();
if(value.trim().equalsIgnoreCase("all")){ if(value.trim().equalsIgnoreCase("all")){

View File

@ -1845,7 +1845,8 @@ final public class Accounts implements AutopsyVisitableItem {
return ICON_BASE_PATH + "WhatsApp.png"; return ICON_BASE_PATH + "WhatsApp.png";
} else { } else {
//there could be a default icon instead... //there could be a default icon instead...
throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName()); return ICON_BASE_PATH + "face.png";
// throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName());
} }
} }
} }

View File

@ -41,6 +41,8 @@ from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel import TskCoreException
from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel import Account
from org.sleuthkit.datamodel import Relationship from org.sleuthkit.datamodel import Relationship
from org.sleuthkit.autopsy.ingest import IngestServices
from org.sleuthkit.autopsy.ingest import ModuleDataEvent
import traceback import traceback
import general import general
@ -78,7 +80,8 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
def __findWWFMessagesInDB(self, databasePath, abstractFile, dataSource): def __findWWFMessagesInDB(self, databasePath, abstractFile, dataSource):
if not databasePath: if not databasePath:
return return
bbartifacts = list()
try: try:
Class.forName("org.sqlite.JDBC"); # load JDBC driver Class.forName("org.sqlite.JDBC"); # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath) connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
@ -115,6 +118,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message)) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message")) attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message"))
attributes.add(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_THREAD_ID, general.MODULE_NAME, user_id))
artifact.addAttributes(attributes) artifact.addAttributes(attributes)
@ -124,6 +128,7 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
# create relationship between accounts # create relationship between accounts
Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [wwfAccountInstance], artifact,Relationship.Type.MESSAGE, created_at); Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(deviceAccountInstance, [wwfAccountInstance], artifact,Relationship.Type.MESSAGE, created_at);
bbartifacts.append(artifact)
try: try:
# index the artifact for keyword search # index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard() blackboard = Case.getCurrentCase().getServices().getBlackboard()
@ -140,6 +145,10 @@ class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
self._logger.log(Level.SEVERE, "Error parsing WWF messages to the blackboard", ex) self._logger.log(Level.SEVERE, "Error parsing WWF messages to the blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc()) self._logger.log(Level.SEVERE, traceback.format_exc())
finally: finally:
if bbartifacts:
IngestServices.getInstance().fireModuleDataEvent(ModuleDataEvent(general.MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE, bbartifacts))
try: try:
if resultSet is not None: if resultSet is not None:
resultSet.close() resultSet.close()