diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/Bundle.properties new file mode 100644 index 0000000000..e7a89556a3 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=EnterpriseArtifactManager diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/README-POSTGRES-TESTING.md b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/README-POSTGRES-TESTING.md new file mode 100644 index 0000000000..ed202b3fe5 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/README-POSTGRES-TESTING.md @@ -0,0 +1,77 @@ +# Setting up the PostgreSQL DB for use in Enterprise Artifact Manager + +## Using Command Line (cmd.exe) + +The easiest way to do this is with the scripts that come with the PostgreSQL server. +Add the PostgreSQL Server bin directory to your path in your user's environment +variables. + + PATH=$PATH;c:\Program Files\PostgreSQL\9.6\bin + +Note: I've had issues getting these Windows PostgreSQL binaries to run in +a cygwin terminal. But they work perfectly in cmd.exe. + +### Create Role + +The role we use has user name "testuser" and password "testpass". + + $ createuser -U postgres -P testuser + +When prompted for a password enter "testpass". + +### Create Database + +The database we use is named "enterpriseartifactmanagerdb". + + $ createdb -T template0 -U postgres -O testuser enterpriseartifactmanagerdb + +### Drop Database + +If we want to reset the database, it's easiest to just drop it. + + $ dropdb -U postgres enterpriseartifactmanagerdb + +### Load the database content from a .sql file + +Before loading a schema.sql file, you must have an empty database named +enterpriseartifactmanagerdb and an existing user named testuser that is the database owner. +Use the schema.sql files to create the tables, indices, and other required settings. + + $ psql -U postgres enterpriseartifactmanagerdb < c:\path\to\schema.sql + +## Using pgAdmin tool + +### Create Role + +Use the right-click menus to create a role named "testuser" with a password of +"testpass". + +### Create Database + +Use the right-click menus to create a database named "enterpriseartifactmanagerdb" +and set testuser as the owner. + +### Load the database content from a .sql file + +Right-click on the enterpriseartifactmanagerdb database and select "CREATE script". +In the new window, delete all of the content and paste in the content of the +schema.sql file. +Click on the lightning icon to execute the contents you just pasted. + +## Notes + +- The schema.sql file does not contain commands to check for the existence of +existing table objects, nor does it drop them before trying to add new ones. +So, it is best to drop and create the database freshly before loading the schema. +- pg_restore cannot load a .sql file. +- The schema.sql file cannot have commands to drop/create the database. + +### Purpose of each schema file + +schema1 - 2 non-normalized tables, no FKs, no enforced uniqueness + +schema2 - 2 non-normalized tables, no FKs, enforced uniqueness on artifacts +- requires PostgreSQL Server ver 9.2+ +- postgresql automatically creates a unique index when a unique constraint is defined, +so there is no need to manually create a unique index for the same column(s). +Doing so would duplicate the automatically-created index. \ No newline at end of file diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/README_MONGODB_TESTING.md b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/README_MONGODB_TESTING.md new file mode 100644 index 0000000000..73c1a33ce1 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/README_MONGODB_TESTING.md @@ -0,0 +1,168 @@ +# Mongo Database for testing + +These instructions assume that you already have MongoDB Server 3.4 installed and +that the server binary is at: + + C:\Program Files\MongoDB\Server\3.4\bin\mongod.exe + +## Using multiple configurations + +It is possible to use a configuration file and even to run MongoDB as a service, +but for our testing, we want one instance that is the default non-secure instance. +And we want a second instance that has auth enabled and has a defined role for +our test user. + +The the easiest way to have multiple instances of MongoDB that have +unique configurations is to have each of them use distinct directories. + +The default directories are: + + C:\data\db + C:\data\log + +So, we need to create a second set of directories at: + + C:\dataauth\db + C:\dataauth\log + +### Create config files, so it is easier to start MongoDB + +The config file is YAML formatted, so do not use TABs. And every +sub-level should be indented 2 spaces from the previous level. + +We will not define the host/port values in the config files, because we assume +that you are using the default port and host values AND +that you will only run ONE of these two instances at a time. +If you want them to run at the same time, include the net.port and net.bindIp +parameters in the config file and make sure they are not both using the same +port/IP pair. + +The first config file is for the default instance, create a new file: + + C:\data\mongod.cfg + +Enter the following content into that file: + + systemLog: + destination: file + path: c:\data\log\mongod.log + storage: + dbPath: c:\data\db + +The second config file is for the auth instance, create a new file: + + C:\dataauth\mongod.cfg + +Enter the following content into that file: + + systemLog: + destination: file + path: c:\dataauth\log\mongod.log + storage: + dbPath: c:\dataauth\db + security: + authorization: enabled + +Note: Ensure Windows did not name your file ending with cfg.txt. You'll +have to go to Folder Options -> View and uncheck the option to hide file extensions +for common files. + +Also, if you will be running mongod using a windows terminal (cmd.exe or powershell), +make sure to use correct Windows path separators (i.e. c:\data\db). +If you are instead using cygwin, make sure to use valid cygwin/unix path +separators (i.e. c:/data/db). + +### To start MongoDB using a config file + +for Windows cmd or ps: + + C:\path\to\bin\mongod.exe --config C:\data\mongod.cfg + +or + + C:\path\to\bin\mongod.exe --config C:\dataauth\mongod.cfg + +for cygwin/unix: + + cd /cygdrive/c/path/to/bin/ + ./mongod.exe --config C:/data/mongod.cfg + +or + + ./mongod.exe --config C:/dataauth/mongod.cfg + + +If it starts correctly, you'll see nothing in the terminal and all logs will +go to the specified log file. +If there is a problem it will display an error in the terminal and fail to start. + +### Setting up the auth'd instance + +The first time you start this mongod instance, you MUST start with auth disabled, +so it will let you create the admin user. Do this in the config file: + + security: + authorization: disabled + +Now start the auth'd mongod in one terminal. + +#### Create admin user + +In a second terminal, connect to that instance with mongo client. + + C:\path\to\mongo.exe + +Enter the following in the mongo client: + + use admin + db.createUser( + { + user: "adminuser", + pwd: "adminpass", + roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] + } + ) + +Logout of the mongo client. +Stop mongod. +Set security.authorization to enabled in the auth'd mongod.cfg. +Start the auth'd mongod. +From now on, you can always start this instance of mongod with authorization +enabled. + +#### Create test user + +In the second terminal, connect to the auth'd instance with mongo client. + + C:\path\to\mongo.exe + +Authenticate as the admin user: + + use admin + db.auth("adminuser", "adminpass") + +Create the test user in the enterpriseartifactmanagerdb database with the readWrite role: + + use enterpriseartifactmanagerdb + db.createUser( + { + user: "testuser", + pwd: "testpass", + roles: [ { role: "readWrite", db: "enterpriseartifactmanagerdb" } ] + } + ) + +Now the EnterpriseArtifactManager code can use the user "testuser" to use the +enterpriseartifactmanagerdb database. This includes creating/dropping indices/collections +along with the usual insert/update/delete commands. + +NOTE: The database where a user is created is that user's "authentication database". +So, when that user needs to authenticate, they need to provide their username, +password, and authentication database. + +### References + +Installation and setup: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/ +Config file: https://docs.mongodb.com/manual/reference/configuration-options/ +Enabling Auth: https://docs.mongodb.com/manual/tutorial/enable-authentication/ +readWrite Role: https://docs.mongodb.com/manual/reference/built-in-roles/#readWrite \ No newline at end of file diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/Bundle.properties new file mode 100644 index 0000000000..2faecfffe2 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/Bundle.properties @@ -0,0 +1,48 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +EnterpriseArtifactManagerCaseEditDetailsPanel.pnCaseMetadata.title=Case Metadata +EnterpriseArtifactManagerCaseEditDetailsPanel.pnOrganization.title=Organization +EnterpriseArtifactManagerCaseEditDetailsPanel.pnExaminer.title=Examiner +EamCaseEditDetailsPanel.lbPointOfContactEmailLabel.text=Email: +EamCaseEditDetailsPanel.lbPointOfContactNameLabel.text=Name: +EamCaseEditDetailsPanel.lbPointOfContactGroupLabel.text=Point of Contact: +EamCaseEditDetailsPanel.lbOrganizationNameLabel.text=Organization Name: +EamCaseEditDetailsPanel.lbCaseNumberText.text=test +EamCaseEditDetailsPanel.taNotesText.text=test +EamCaseEditDetailsPanel.lbCeationDateText.text=test +EamCaseEditDetailsPanel.lbCaseNameText.text=test +EamCaseEditDetailsPanel.lbNotesLabel.text=Notes: +EamCaseEditDetailsPanel.lbCaseUUIDText.text=test +EamCaseEditDetailsPanel.tfExaminerPhoneText.text=test +EamCaseEditDetailsPanel.lbExaminerPhoneLabel.text=Phone: +EamCaseEditDetailsPanel.lbCaseUUIDLabel.text=Case UUID: +EamCaseEditDetailsPanel.lbCaseNumberLabel.text=Case Number: +EamCaseEditDetailsPanel.tfExaminerEmailText.text=test +EamCaseEditDetailsPanel.lbCreationDateLabel.text=Creation Date: +EamCaseEditDetailsPanel.lbExaminerEmailLabel.text=Email: +EamCaseEditDetailsPanel.lbCaseNameLabel.text=Case Name: +EamCaseEditDetailsPanel.tfExaminerNameText.text=test +EamCaseEditDetailsPanel.lbExaminerNameLabel.text=Name: +EamCaseEditDetailsPanel.bnApply.text=Apply +EamCaseEditDetailsPanel.bnClose.text=Close +EamCaseEditDetailsPanel.bnNewOrganization.text=New Organization +EamCaseEditDetailsPanel.lbPointOfContactPhoneText.text=test +EamCaseEditDetailsPanel.lbPointOfContactEmailText.text=test +EamCaseEditDetailsPanel.lbPointOfContactNameText.text=test +EamCaseEditDetailsPanel.lbPointOfContactPhoneLabel.text=Phone: diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamCaseEditDetailsPanel.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamCaseEditDetailsPanel.form new file mode 100644 index 0000000000..a6ae3b707b --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamCaseEditDetailsPanel.form @@ -0,0 +1,511 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamCaseEditDetailsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamCaseEditDetailsPanel.java new file mode 100644 index 0000000000..bbab379a6d --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamCaseEditDetailsPanel.java @@ -0,0 +1,592 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.actions; + +import java.awt.Cursor; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import javax.swing.JComboBox; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamCase; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamOrganization; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel.EamAddNewOrganizationDialog; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Handle editing details of cases within the Enterprise Artifact Manager + */ +public class EamCaseEditDetailsPanel extends JPanel { + + private final static Logger LOGGER = Logger.getLogger(EamCaseEditDetailsPanel.class.getName()); + private final EamCase eamCase; + private final EamDb dbManager; + private Boolean contentChanged = false; + private final Collection textBoxes = new ArrayList<>(); + private final Collection textAreas = new ArrayList<>(); + private final TextBoxChangedListener textBoxChangedListener = new TextBoxChangedListener(); + private EamOrganization selectedOrg = null; + private List orgs = null; + + /** + * Creates new form EnterpriseArtifactManagerCasedEditDetailsForm + */ + public EamCaseEditDetailsPanel(EamCase eamCase) { + this.eamCase = eamCase; + this.dbManager = EamDb.getInstance(); + initComponents(); + customizeComponents(); + loadData(); + } + + private void customizeComponents() { + bnApply.setEnabled(false); + setTextBoxListeners(); + setTextAreaListeners(); + } + + private void setTextBoxListeners() { + // Register for notifications when the text boxes get updated. + textBoxes.add(tfExaminerNameText); + textBoxes.add(tfExaminerEmailText); + textBoxes.add(tfExaminerPhoneText); + addTextFieldDocumentListeners(textBoxes, textBoxChangedListener); + } + + private void setTextAreaListeners() { + // Register for notifications when the text areas get updated. + textAreas.add(taNotesText); + addTextAreaDocumentListeners(textAreas, textBoxChangedListener); + } + + /** + * Adds a change listener to a collection of text fields. + * + * @param textFields The text fields. + * @param listener The change listener. + */ + private static void addTextFieldDocumentListeners(Collection textFields, TextBoxChangedListener listener) { + textFields.forEach((textField) -> { + textField.getDocument().addDocumentListener(listener); + }); + } + + /** + * Adds a change listener to a collection of text areas. + * + * @param textAreas The text areas. + * @param listener The change listener. + */ + private static void addTextAreaDocumentListeners(Collection textAreas, TextBoxChangedListener listener) { + textAreas.forEach((textArea) -> { + textArea.getDocument().addDocumentListener(listener); + }); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + bnClose = new javax.swing.JButton(); + bnApply = new javax.swing.JButton(); + pnCaseMetadata = new javax.swing.JPanel(); + lbCaseNameLabel = new javax.swing.JLabel(); + lbCreationDateLabel = new javax.swing.JLabel(); + lbCaseNumberLabel = new javax.swing.JLabel(); + lbCaseUUIDLabel = new javax.swing.JLabel(); + lbCaseUUIDText = new javax.swing.JLabel(); + lbCaseNameText = new javax.swing.JLabel(); + lbCeationDateText = new javax.swing.JLabel(); + lbCaseNumberText = new javax.swing.JLabel(); + pnOrganization = new javax.swing.JPanel(); + lbOrganizationNameLabel = new javax.swing.JLabel(); + comboBoxOrgName = new javax.swing.JComboBox<>(); + lbPointOfContactGroupLabel = new javax.swing.JLabel(); + lbPointOfContactNameLabel = new javax.swing.JLabel(); + lbPointOfContactEmailLabel = new javax.swing.JLabel(); + lbPointOfContactPhoneLabel = new javax.swing.JLabel(); + lbPointOfContactNameText = new javax.swing.JLabel(); + lbPointOfContactEmailText = new javax.swing.JLabel(); + lbPointOfContactPhoneText = new javax.swing.JLabel(); + bnNewOrganization = new javax.swing.JButton(); + pnExaminer = new javax.swing.JPanel(); + lbExaminerNameLabel = new javax.swing.JLabel(); + tfExaminerNameText = new javax.swing.JTextField(); + lbExaminerEmailLabel = new javax.swing.JLabel(); + tfExaminerEmailText = new javax.swing.JTextField(); + lbExaminerPhoneLabel = new javax.swing.JLabel(); + tfExaminerPhoneText = new javax.swing.JTextField(); + lbNotesLabel = new javax.swing.JLabel(); + jScrollPane2 = new javax.swing.JScrollPane(); + taNotesText = new javax.swing.JTextArea(); + + org.openide.awt.Mnemonics.setLocalizedText(bnClose, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.bnClose.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnApply, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.bnApply.text")); // NOI18N + bnApply.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnApplyActionPerformed(evt); + } + }); + + pnCaseMetadata.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "CorrelationEngineCaseEditDetailsPanel.pnCaseMetadata.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCaseNameLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCaseNameLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCreationDateLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCreationDateLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCaseNumberLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCaseNumberLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCaseUUIDLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCaseUUIDLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCaseUUIDText, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCaseUUIDText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCaseNameText, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCaseNameText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCeationDateText, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCeationDateText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbCaseNumberText, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbCaseNumberText.text")); // NOI18N + + javax.swing.GroupLayout pnCaseMetadataLayout = new javax.swing.GroupLayout(pnCaseMetadata); + pnCaseMetadata.setLayout(pnCaseMetadataLayout); + pnCaseMetadataLayout.setHorizontalGroup( + pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnCaseMetadataLayout.createSequentialGroup() + .addGap(25, 25, 25) + .addGroup(pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(lbCaseNumberLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbCreationDateLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbCaseNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 114, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbCaseUUIDLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbCaseNameText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbCeationDateText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbCaseNumberText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbCaseUUIDText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + pnCaseMetadataLayout.setVerticalGroup( + pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnCaseMetadataLayout.createSequentialGroup() + .addGroup(pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbCaseUUIDLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbCaseUUIDText)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbCaseNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbCaseNameText, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbCreationDateLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbCeationDateText, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(pnCaseMetadataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbCaseNumberLabel) + .addComponent(lbCaseNumberText, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(37, 37, 37)) + ); + + pnOrganization.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "CorrelationEngineCaseEditDetailsPanel.pnOrganization.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbOrganizationNameLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbOrganizationNameLabel.text")); // NOI18N + + comboBoxOrgName.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + comboBoxOrgName.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + comboBoxOrgNameActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(lbPointOfContactGroupLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbPointOfContactGroupLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPointOfContactNameLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbPointOfContactNameLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPointOfContactEmailLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbPointOfContactEmailLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPointOfContactPhoneLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbPointOfContactPhoneLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPointOfContactNameText, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbPointOfContactNameText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPointOfContactEmailText, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbPointOfContactEmailText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPointOfContactPhoneText, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbPointOfContactPhoneText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnNewOrganization, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.bnNewOrganization.text")); // NOI18N + bnNewOrganization.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnNewOrganizationActionPerformed(evt); + } + }); + + javax.swing.GroupLayout pnOrganizationLayout = new javax.swing.GroupLayout(pnOrganization); + pnOrganization.setLayout(pnOrganizationLayout); + pnOrganizationLayout.setHorizontalGroup( + pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnOrganizationLayout.createSequentialGroup() + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnOrganizationLayout.createSequentialGroup() + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, pnOrganizationLayout.createSequentialGroup() + .addGap(25, 25, 25) + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(lbPointOfContactGroupLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbOrganizationNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addGroup(pnOrganizationLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(lbPointOfContactPhoneLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 88, Short.MAX_VALUE) + .addComponent(lbPointOfContactEmailLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbPointOfContactNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGap(18, 18, 18) + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbPointOfContactNameText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbPointOfContactEmailText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(lbPointOfContactPhoneText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(comboBoxOrgName, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnOrganizationLayout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(bnNewOrganization))) + .addContainerGap()) + ); + pnOrganizationLayout.setVerticalGroup( + pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnOrganizationLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbOrganizationNameLabel) + .addComponent(comboBoxOrgName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lbPointOfContactGroupLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbPointOfContactNameLabel) + .addComponent(lbPointOfContactNameText)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbPointOfContactEmailLabel) + .addComponent(lbPointOfContactEmailText)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnOrganizationLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbPointOfContactPhoneLabel) + .addComponent(lbPointOfContactPhoneText)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(bnNewOrganization) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pnExaminer.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "CorrelationEngineCaseEditDetailsPanel.pnExaminer.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbExaminerNameLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbExaminerNameLabel.text")); // NOI18N + + tfExaminerNameText.setText(org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.tfExaminerNameText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbExaminerEmailLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbExaminerEmailLabel.text")); // NOI18N + + tfExaminerEmailText.setText(org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.tfExaminerEmailText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbExaminerPhoneLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbExaminerPhoneLabel.text")); // NOI18N + + tfExaminerPhoneText.setText(org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.tfExaminerPhoneText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbNotesLabel, org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.lbNotesLabel.text")); // NOI18N + + taNotesText.setColumns(20); + taNotesText.setRows(5); + taNotesText.setText(org.openide.util.NbBundle.getMessage(EamCaseEditDetailsPanel.class, "EamCaseEditDetailsPanel.taNotesText.text")); // NOI18N + jScrollPane2.setViewportView(taNotesText); + + javax.swing.GroupLayout pnExaminerLayout = new javax.swing.GroupLayout(pnExaminer); + pnExaminer.setLayout(pnExaminerLayout); + pnExaminerLayout.setHorizontalGroup( + pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnExaminerLayout.createSequentialGroup() + .addGap(28, 28, 28) + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbExaminerEmailLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 85, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbExaminerNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbExaminerPhoneLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbNotesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 87, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(25, 25, 25) + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnExaminerLayout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(tfExaminerNameText, javax.swing.GroupLayout.PREFERRED_SIZE, 274, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + .addGroup(pnExaminerLayout.createSequentialGroup() + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 273, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(tfExaminerEmailText, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE) + .addComponent(tfExaminerPhoneText))) + .addGap(0, 0, Short.MAX_VALUE)))) + ); + pnExaminerLayout.setVerticalGroup( + pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnExaminerLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbExaminerNameLabel) + .addComponent(tfExaminerNameText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(tfExaminerEmailText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbExaminerEmailLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(tfExaminerPhoneText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbExaminerPhoneLabel)) + .addGap(24, 24, 24) + .addGroup(pnExaminerLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbNotesLabel) + .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(bnApply) + .addGap(18, 18, 18) + .addComponent(bnClose)) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pnCaseMetadata, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pnOrganization, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pnExaminer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(23, 23, 23) + .addComponent(pnCaseMetadata, javax.swing.GroupLayout.PREFERRED_SIZE, 119, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pnOrganization, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pnExaminer, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(16, 16, 16) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnApply) + .addComponent(bnClose)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void bnApplyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnApplyActionPerformed + // write content to db + if (contentChanged) { + updateEnterpriseArtifactManagerCase(); + updateDb(); + contentChanged = false; + } + applyButtonEnable(false); + + }//GEN-LAST:event_bnApplyActionPerformed + + private void bnNewOrganizationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnNewOrganizationActionPerformed + EamAddNewOrganizationDialog dialogO = new EamAddNewOrganizationDialog(); + // update the combobox options + if (dialogO.isChanged()) { + loadData(); + contentChanged = true; + applyButtonEnable(true); + } + }//GEN-LAST:event_bnNewOrganizationActionPerformed + + @SuppressWarnings({"unchecked"}) + private void comboBoxOrgNameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboBoxOrgNameActionPerformed + JComboBox cb = (JComboBox) evt.getSource(); + String orgName = (String) cb.getSelectedItem(); + if (null == orgName) { + return; + } + + for (EamOrganization org : orgs) { + if (org.getName().equals(orgName)) { + selectedOrg = org; + lbPointOfContactNameText.setText(selectedOrg.getPocName()); + lbPointOfContactEmailText.setText(selectedOrg.getPocEmail()); + lbPointOfContactPhoneText.setText(selectedOrg.getPocPhone()); + contentChanged = true; + applyButtonEnable(true); + return; + } + } + }//GEN-LAST:event_comboBoxOrgNameActionPerformed + + private Boolean applyButtonEnable(Boolean enable) { + bnApply.setEnabled(enable); + return enable; + } + + /* + * Add ActionListener to closeButton + */ + public void addCloseButtonAction(ActionListener al) { + bnClose.addActionListener(al); + } + + private void loadCaseMetaData() { + lbCaseUUIDText.setText(eamCase.getCaseUUID()); + lbCaseNameText.setText(eamCase.getDisplayName()); + lbCeationDateText.setText(eamCase.getCreationDate()); + lbCaseNumberText.setText(eamCase.getCaseNumber()); + + } + + private void loadExaminerData() { + tfExaminerNameText.setText(eamCase.getExaminerName()); + tfExaminerEmailText.setText(eamCase.getExaminerEmail()); + tfExaminerPhoneText.setText(eamCase.getExaminerPhone()); + taNotesText.setText(eamCase.getNotes()); + } + + private void loadOrganizationData() { + comboBoxOrgName.removeAllItems(); + try { + orgs = dbManager.getOrganizations(); + orgs.forEach((org) -> { + comboBoxOrgName.addItem(org.getName()); + }); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Failure populating combobox with organizations.", ex); + } + + selectedOrg = orgs.get(0); + + lbPointOfContactNameText.setText(selectedOrg.getPocName()); + lbPointOfContactEmailText.setText(selectedOrg.getPocEmail()); + lbPointOfContactPhoneText.setText(selectedOrg.getPocPhone()); + } + + private void loadData() { + loadCaseMetaData(); + loadExaminerData(); + loadOrganizationData(); + } + + /** + * Save changed value from text fields and text areas into the EamCase + * object. + */ + private void updateEnterpriseArtifactManagerCase() { + eamCase.setOrg(selectedOrg); + eamCase.setExaminerName(tfExaminerNameText.getText()); + eamCase.setExaminerEmail(tfExaminerEmailText.getText()); + eamCase.setExaminerPhone(tfExaminerPhoneText.getText()); + eamCase.setNotes(taNotesText.getText()); + } + + private void updateDb() { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + if (!dbManager.isEnabled()) { + LOGGER.log(Level.SEVERE, "Enteprise artifact manager database not enabled"); // NON-NLS + return; + } + + try { + dbManager.updateCase(eamCase); + } catch (IllegalArgumentException | EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to enterprise artifact manager database", ex); // NON-NLS + } finally { + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + + /** + * Used to listen for changes in text areas/boxes. Let the panel know text + * content has changed. + */ + private class TextBoxChangedListener implements DocumentListener { + + @Override + public void changedUpdate(DocumentEvent e) { + setChanged(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + setChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + setChanged(); + } + + private void setChanged() { + contentChanged = true; + applyButtonEnable(true); + } + } + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnApply; + private javax.swing.JButton bnClose; + private javax.swing.JButton bnNewOrganization; + private javax.swing.JComboBox comboBoxOrgName; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JLabel lbCaseNameLabel; + private javax.swing.JLabel lbCaseNameText; + private javax.swing.JLabel lbCaseNumberLabel; + private javax.swing.JLabel lbCaseNumberText; + private javax.swing.JLabel lbCaseUUIDLabel; + private javax.swing.JLabel lbCaseUUIDText; + private javax.swing.JLabel lbCeationDateText; + private javax.swing.JLabel lbCreationDateLabel; + private javax.swing.JLabel lbExaminerEmailLabel; + private javax.swing.JLabel lbExaminerNameLabel; + private javax.swing.JLabel lbExaminerPhoneLabel; + private javax.swing.JLabel lbNotesLabel; + private javax.swing.JLabel lbOrganizationNameLabel; + private javax.swing.JLabel lbPointOfContactEmailLabel; + private javax.swing.JLabel lbPointOfContactEmailText; + private javax.swing.JLabel lbPointOfContactGroupLabel; + private javax.swing.JLabel lbPointOfContactNameLabel; + private javax.swing.JLabel lbPointOfContactNameText; + private javax.swing.JLabel lbPointOfContactPhoneLabel; + private javax.swing.JLabel lbPointOfContactPhoneText; + private javax.swing.JPanel pnCaseMetadata; + private javax.swing.JPanel pnExaminer; + private javax.swing.JPanel pnOrganization; + private javax.swing.JTextArea taNotesText; + private javax.swing.JTextField tfExaminerEmailText; + private javax.swing.JTextField tfExaminerNameText; + private javax.swing.JTextField tfExaminerPhoneText; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamEditCaseInfoAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamEditCaseInfoAction.java new file mode 100644 index 0000000000..fddd47272a --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/actions/EamEditCaseInfoAction.java @@ -0,0 +1,140 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.actions; + +import java.awt.Dimension; +import java.awt.HeadlessException; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.Action; +import javax.swing.JDialog; +import javax.swing.JFrame; +import org.openide.awt.ActionID; +import org.openide.awt.ActionReference; +import org.openide.awt.ActionRegistration; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle.Messages; +import org.openide.util.actions.CallableSystemAction; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamCase; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Action to update case details in enterprise artifact manager database + */ +@ActionID( + category = "Case", + id = "org.sleuthkit.enterpriseartifactmanager.actions.EnterpriseArtifactManagerCaseEditCaseInfoAction" +) +@ActionRegistration( + displayName = "#CTL_EnterpriseArtifactManagerCaseEditCaseInfo", + lazy = false +) +@ActionReference(path = "Menu/Case", position = 650, separatorAfter = 824) +@Messages("CTL_EnterpriseArtifactManagerCaseEditCaseInfo=Enterprise Artifact Manager Case Details") +public final class EamEditCaseInfoAction extends CallableSystemAction implements ActionListener { + + private final static Logger LOGGER = Logger.getLogger(EamEditCaseInfoAction.class.getName()); + + private static JDialog popUpWindow; + + EamEditCaseInfoAction() { + putValue(Action.NAME, Bundle.CTL_EnterpriseArtifactManagerCaseEditCaseInfo()); // put the action Name + this.setEnabled(true); + Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + popUpWindow = null; + } + }); + } + + @Override + public boolean isEnabled() { + boolean enabled = Boolean.valueOf(ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.enabled")); // NON-NLS + return enabled && Case.isCaseOpen(); + } + + @Override + public void actionPerformed(ActionEvent e) { + performAction(); + } + + @Override + @Messages({"EnterpriseArtifactManagerCaseEditDetails.window.title=Edit Case Details"}) + public void performAction() { + + if (popUpWindow == null) { + String curCaseDisplayName = Case.getCurrentCase().getDisplayName(); + // TODO: replace with caseUUID once it is implemented + String curCaseUUID = curCaseDisplayName; + + // create the popUp window for it + String title = Bundle.EnterpriseArtifactManagerCaseEditDetails_window_title(); + popUpWindow = new JDialog((JFrame) WindowManager.getDefault().getMainWindow(), title, false); + try { + // query case details + EamCase eamCase = EamDb.getInstance().getCaseDetails(curCaseUUID); + + EamCaseEditDetailsPanel caseInformationPanel = new EamCaseEditDetailsPanel(eamCase); + caseInformationPanel.addCloseButtonAction((ActionEvent e) -> { + popUpWindow.dispose(); + }); + + popUpWindow.add(caseInformationPanel); + popUpWindow.setResizable(true); + popUpWindow.pack(); + + // set the location of the popUp Window on the center of the screen + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + double w = popUpWindow.getSize().getWidth(); + double h = popUpWindow.getSize().getHeight(); + popUpWindow.setLocation((int) ((screenDimension.getWidth() - w) / 2), (int) ((screenDimension.getHeight() - h) / 2)); + + popUpWindow.setVisible(true); + } catch (HeadlessException ex) { + LOGGER.log(Level.WARNING, "Error displaying Enterprise Artifact Manager Case Properties window.", ex); //NON-NLS + } catch (EamDbException ex) { + LOGGER.log(Level.WARNING, "Error connecting to Enterprise Artifact Manager databaes.", ex); // NON-NLS + } + } + popUpWindow.setVisible(true); + popUpWindow.toFront(); + + } + + @Override + public String getName() { + return Bundle.CTL_EnterpriseArtifactManagerCaseEditCaseInfo(); + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/Bundle.properties new file mode 100644 index 0000000000..f1ab298155 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/Bundle.properties @@ -0,0 +1,5 @@ +DataContentViewerOtherCases.selectAllMenuItem.text=Select All +DataContentViewerOtherCases.showCaseDetailsMenuItem.text=Show Case Details +DataContentViewerOtherCases.table.toolTip.text=Click column name to sort. Right-click on the table for more options. +DataContentViewerOtherCases.exportToCSVMenuItem.text=Export Selected Rows to CSV +DataContentViewerOtherCases.showCommonalityMenuItem.text=Show Commonality diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCases.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCases.form new file mode 100644 index 0000000000..a807f4c8af --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCases.form @@ -0,0 +1,185 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCases.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCases.java new file mode 100644 index 0000000000..d7eab67f14 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCases.java @@ -0,0 +1,706 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.contentviewer; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import java.util.stream.Collectors; +import javax.swing.JFileChooser; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import static javax.swing.JOptionPane.DEFAULT_OPTION; +import static javax.swing.JOptionPane.PLAIN_MESSAGE; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import org.openide.nodes.Node; +import org.openide.util.NbBundle.Messages; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactInstance; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamCase; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamGlobalFileInstance; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * View correlation results from other cases + */ +@ServiceProvider(service = DataContentViewer.class, position = 8) +@Messages({"DataContentViewerOtherCases.title=Other Cases", + "DataContentViewerOtherCases.toolTip=Displays instances of the selected file/artifact from other cases.",}) +public class DataContentViewerOtherCases extends javax.swing.JPanel implements DataContentViewer { + + private final static Logger LOGGER = Logger.getLogger(DataContentViewerOtherCases.class.getName()); + + private final DataContentViewerOtherCasesTableModel tableModel; + private final Collection correlatedArtifacts; + + /** + * Creates new form DataContentViewerOtherCases + */ + public DataContentViewerOtherCases() { + this.tableModel = new DataContentViewerOtherCasesTableModel(); + this.correlatedArtifacts = new ArrayList<>(); + + initComponents(); + customizeComponents(); + readSettings(); + } + + private void customizeComponents() { + ActionListener actList = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JMenuItem jmi = (JMenuItem) e.getSource(); + if (jmi.equals(selectAllMenuItem)) { + otherCasesTable.selectAll(); + } else if (jmi.equals(showCaseDetailsMenuItem)) { + showCaseDetails(otherCasesTable.getSelectedRow()); + } else if (jmi.equals(exportToCSVMenuItem)) { + saveToCSV(); + } else if (jmi.equals(showCommonalityMenuItem)) { + showCommonalityDetails(); + } + } + }; + + exportToCSVMenuItem.addActionListener(actList); + selectAllMenuItem.addActionListener(actList); + showCaseDetailsMenuItem.addActionListener(actList); + showCommonalityMenuItem.addActionListener(actList); + + // Set background of every nth row as light grey. + TableCellRenderer renderer = new DataContentViewerOtherCasesTableCellRenderer(); + otherCasesTable.setDefaultRenderer(Object.class, renderer); + tableStatusPanelLabel.setVisible(false); + } + + @Messages({"DataContentViewerOtherCases.correlatedArtifacts.isEmpty=There are no files or artifacts to correlate.", + "# {0} - commonality percentage", + "# {1} - artifact type", + "# {2} - artifact value", + "DataContentViewerOtherCases.correlatedArtifacts.byType={0}% for Artifact Type: {1} and Artifact Value: {2}.\n", + "DataContentViewerOtherCases.correlatedArtifacts.title=Commonality Percentages", + "DataContentViewerOtherCases.correlatedArtifacts.failed=Failed to get commonality details."}) + private void showCommonalityDetails() { + if (correlatedArtifacts.isEmpty()) { + JOptionPane.showConfirmDialog(showCommonalityMenuItem, + Bundle.DataContentViewerOtherCases_correlatedArtifacts_isEmpty(), + Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(), + DEFAULT_OPTION, PLAIN_MESSAGE); + } else { + StringBuilder msg = new StringBuilder(); + int percentage; + try { + EamDb dbManager = EamDb.getInstance(); + for (EamArtifact eamArtifact : correlatedArtifacts) { + percentage = dbManager.getCommonalityPercentageForTypeValue(eamArtifact); + msg.append(Bundle.DataContentViewerOtherCases_correlatedArtifacts_byType(percentage, + eamArtifact.getArtifactType().getName(), + eamArtifact.getArtifactValue())); + } + JOptionPane.showConfirmDialog(showCommonalityMenuItem, + msg.toString(), + Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(), + DEFAULT_OPTION, PLAIN_MESSAGE); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting commonality details.", ex); + JOptionPane.showConfirmDialog(showCommonalityMenuItem, + Bundle.DataContentViewerOtherCases_correlatedArtifacts_failed(), + Bundle.DataContentViewerOtherCases_correlatedArtifacts_title(), + DEFAULT_OPTION, ERROR_MESSAGE); + } + } + } + + @Messages({"DataContentViewerOtherCases.caseDetailsDialog.notSelected=No Row Selected", + "DataContentViewerOtherCases.caseDetailsDialog.noDetails=No details for this case.", + "DataContentViewerOtherCases.caseDetailsDialog.noCaseNameError=Error"}) + private void showCaseDetails(int selectedRowViewIdx) { + String caseDisplayName = Bundle.DataContentViewerOtherCases_caseDetailsDialog_noCaseNameError(); + try { + if (-1 != selectedRowViewIdx) { + EamDb dbManager = EamDb.getInstance(); + int selectedRowModelIdx = otherCasesTable.convertRowIndexToModel(selectedRowViewIdx); + EamArtifact eamArtifact = (EamArtifact) tableModel.getRow(selectedRowModelIdx); + EamCase eamCasePartial = eamArtifact.getInstances().get(0).getEamCase(); + caseDisplayName = eamCasePartial.getDisplayName(); + // query case details + EamCase eamCase = dbManager.getCaseDetails(eamCasePartial.getCaseUUID()); + if (eamCase == null) { + JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, + Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(), + caseDisplayName, + DEFAULT_OPTION, PLAIN_MESSAGE); + return; + } + + // display case details + JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, + eamCase.getCaseDetailsOptionsPaneDialog(), + caseDisplayName, + DEFAULT_OPTION, PLAIN_MESSAGE); + } else { + JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, + Bundle.DataContentViewerOtherCases_caseDetailsDialog_notSelected(), + caseDisplayName, + DEFAULT_OPTION, PLAIN_MESSAGE); + } + } catch (EamDbException ex) { + JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, + Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(), + caseDisplayName, + DEFAULT_OPTION, PLAIN_MESSAGE); + } + } + + private void saveToCSV() { + if (0 != otherCasesTable.getSelectedRowCount()) { + Calendar now = Calendar.getInstance(); + String fileName = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS_other_cases.csv", now); + CSVFileChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); + CSVFileChooser.setSelectedFile(new File(fileName)); + CSVFileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv")); + + int returnVal = CSVFileChooser.showSaveDialog(otherCasesTable); + if (returnVal == JFileChooser.APPROVE_OPTION) { + + File selectedFile = CSVFileChooser.getSelectedFile(); + if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS + selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS + } + + writeSelectedRowsToFileAsCSV(selectedFile); + } + } + } + + private void writeSelectedRowsToFileAsCSV(File destFile) { + StringBuilder content; + int[] selectedRowViewIndices = otherCasesTable.getSelectedRows(); + int colCount = tableModel.getColumnCount(); + + try (BufferedWriter writer = Files.newBufferedWriter(destFile.toPath())) { + + // write column names + content = new StringBuilder(""); + for (int colIdx = 0; colIdx < colCount; colIdx++) { + content.append('"').append(tableModel.getColumnName(colIdx)).append('"'); + if (colIdx < (colCount - 1)) { + content.append(","); + } + } + + content.append(System.getProperty("line.separator")); + writer.write(content.toString()); + + // write rows + for (int rowViewIdx : selectedRowViewIndices) { + content = new StringBuilder(""); + for (int colIdx = 0; colIdx < colCount; colIdx++) { + int rowModelIdx = otherCasesTable.convertRowIndexToModel(rowViewIdx); + content.append('"').append(tableModel.getValueAt(rowModelIdx, colIdx)).append('"'); + if (colIdx < (colCount - 1)) { + content.append(","); + } + } + content.append(System.getProperty("line.separator")); + writer.write(content.toString()); + } + + } catch (IOException ex) { + LOGGER.log(Level.SEVERE, "Error writing selected rows to CSV.", ex); + } + } + + /** + * Read the module settings from the config file and reset the table model. + */ + private boolean readSettings() { + // start with empty table + tableModel.clearTable(); + correlatedArtifacts.clear(); + + return true; + } + + @Override + public String getTitle() { + return Bundle.DataContentViewerOtherCases_title(); + } + + @Override + public String getToolTip() { + return Bundle.DataContentViewerOtherCases_toolTip(); + } + + @Override + public DataContentViewer createInstance() { + return new DataContentViewerOtherCases(); + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + readSettings(); + } + + @Override + public int isPreferred(Node node) { + return 1; + } + + /** + * Get the associated BlackboardArtifact from a node, if it exists. + * + * @param node The node + * + * @return The associated BlackboardArtifact, or null + */ + private BlackboardArtifact getBlackboardArtifactFromNode(Node node) { + BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class); + BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class); + + if (nodeBbArtifactTag != null) { + return nodeBbArtifactTag.getArtifact(); + } else if (nodeBbArtifact != null) { + return nodeBbArtifact; + } + + return null; + } + + /** + * Get the associated AbstractFile from a node, if it exists. + * + * @param node The node + * + * @return The associated AbstractFile, or null + */ + private AbstractFile getAbstractFileFromNode(Node node) { + BlackboardArtifactTag nodeBbArtifactTag = node.getLookup().lookup(BlackboardArtifactTag.class); + ContentTag nodeContentTag = node.getLookup().lookup(ContentTag.class); + BlackboardArtifact nodeBbArtifact = node.getLookup().lookup(BlackboardArtifact.class); + AbstractFile nodeAbstractFile = node.getLookup().lookup(AbstractFile.class); + + if (nodeBbArtifactTag != null) { + Content content = nodeBbArtifactTag.getContent(); + if (content instanceof AbstractFile) { + return (AbstractFile) content; + } + } else if (nodeContentTag != null) { + Content content = nodeContentTag.getContent(); + if (content instanceof AbstractFile) { + return (AbstractFile) content; + } + } else if (nodeBbArtifact != null) { + Content content; + try { + content = nodeBbArtifact.getSleuthkitCase().getContentById(nodeBbArtifact.getObjectID()); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error retrieving blackboard artifact", ex); // NON-NLS + return null; + } + + if (content instanceof AbstractFile) { + return (AbstractFile) content; + } + } else if (nodeAbstractFile != null) { + return nodeAbstractFile; + } + + return null; + } + + /** + * Scan a Node for blackboard artifacts / content that we can correlate on + * and create the corresponding enterprise artifact manager artifacts for + * display + * + * @param node The node to view + * + * @return A collection of enterprise artifact manager artifacts to display + */ + private Collection getArtifactsFromCorrelatableAttributes(Node node) { + Collection ret = new ArrayList<>(); + + /* + * If the user selected a blackboard artifact or tag of a BB artifact, + * correlate both the artifact and the associated file. If the user + * selected a file, correlate only the file + */ + BlackboardArtifact bbArtifact = getBlackboardArtifactFromNode(node); + AbstractFile abstractFile = getAbstractFileFromNode(node); + List artifactTypes = null; + try { + EamDb dbManager = EamDb.getInstance(); + artifactTypes = dbManager.getCorrelationArtifactTypes(); + if (bbArtifact != null) { + EamArtifact eamArtifact = EamArtifactUtil.fromBlackboardArtifact(bbArtifact, false, artifactTypes, false); + if (eamArtifact != null) { + ret.add(eamArtifact); + } + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error retrieving correlation artifact types", ex); // NON-NLS + } + + if (abstractFile != null) { + String md5 = abstractFile.getMd5Hash(); + if (md5 != null && !md5.isEmpty() && null != artifactTypes && !artifactTypes.isEmpty()) { + for (EamArtifact.Type aType : artifactTypes) { + if (aType.getName().equals("FILES")) { + ret.add(new EamArtifact(aType, md5)); + break; + } + } + } + } + + return ret; + } + + /** + * Given a node, return the associated data source + * + * @param node The node + * + * @return The name of the data source + */ + private String getDataSourceNameFromNode(Node node) { + AbstractFile af = getAbstractFileFromNode(node); + try { + if (af != null) { + return af.getDataSource().getName(); + } + } catch (TskException ex) { + return ""; + } + + return ""; + } + + /** + * Given a node, return the associated data source's device ID + * + * @param node The node + * + * @return The ID of the data source's device + */ + private String getDeviceIdFromNode(Node node) { + AbstractFile af = getAbstractFileFromNode(node); + try { + if (af != null) { + return Case.getCurrentCase().getSleuthkitCase().getDataSource(af.getDataSource().getId()).getDeviceId(); + } + } catch (TskException ex) { + return ""; + } + + return ""; + } + + /** + * Query the CDB for artifact instances from other cases correlated to the + * given enterprise artifact manager artifact. + * + * @param eamArtifact The artifact to correlate against + * + * @return A collection of correlated artifact instances from other cases + */ + private Collection getCorrelatedInstances(EamArtifact eamArtifact, String dataSourceName, String deviceId) { + String caseUUID = Case.getCurrentCase().getName(); + try { + EamDb dbManager = EamDb.getInstance(); + Collection artifactInstances = dbManager.getArtifactInstancesByTypeValue(eamArtifact).stream() + .filter(artifactInstance -> !artifactInstance.getEamCase().getCaseUUID().equals(caseUUID) + || !artifactInstance.getEamDataSource().getName().equals(dataSourceName) + || !artifactInstance.getEamDataSource().getDeviceID().equals(deviceId)) + .collect(Collectors.toList()); + return artifactInstances; + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS + } + + return Collections.emptyList(); + } + + /** + * Get the Global File Instances matching the given eamArtifact and convert + * them to Enterprise Artifact Manager Artifact Instancess. + * + * @param eamArtifact Artifact to use for ArtifactTypeEnum matching + * + * @return List of Enterprise Artifact Manager Artifact Instances, empty + * list if none found + */ + public Collection getGlobalFileInstancesAsArtifactInstances(EamArtifact eamArtifact) { + Collection eamArtifactInstances = new ArrayList<>(); + try { + EamDb dbManager = EamDb.getInstance(); + if (dbManager.getCorrelationArtifactTypeByName("FILES") == eamArtifact.getArtifactType()) { + try { + Collection eamGlobalFileInstances = dbManager.getGlobalFileInstancesByHash(eamArtifact.getArtifactValue()); + for (EamGlobalFileInstance eamGlobalFileInstance : eamGlobalFileInstances) { + eamArtifactInstances.add(new EamArtifactInstance( + null, null, "", eamGlobalFileInstance.getComment(), eamGlobalFileInstance.getKnownStatus(), EamArtifactInstance.GlobalStatus.GLOBAL + )); + } + return eamArtifactInstances; + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting global file instances from database.", ex); // NON-NLS + } + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting correlation artifact type MD5 from database.", ex); // NON-NLS + } + return Collections.emptyList(); + } + + @Override + public boolean isSupported(Node node) { + EamDb dbManager = EamDb.getInstance(); + if (!dbManager.isEnabled()) { + return false; + } + + // Is supported if this node has correlatable content (File, BlackboardArtifact) + return !getArtifactsFromCorrelatableAttributes(node).isEmpty(); + } + + @Override + @Messages({"DataContentViewerOtherCases.table.nodbconnection=Cannot connect to enterprise artifact manager database."}) + public void setNode(Node node) { + EamDb dbManager = EamDb.getInstance(); + if (!dbManager.isEnabled()) { + return; + } + + populateTable(node); + } + + /** + * Load the correlatable data into the table model. If there is no data + * available display the message on the status panel. + * + * @param node The node being viewed. + */ + @Messages({"DataContentViewerOtherCases.table.isempty=There are no associated artifacts or files from other cases to display.", + "DataContentViewerOtherCases.table.noArtifacts=Correlation cannot be performed on the selected file; likely missing MD5 hash."}) + private void populateTable(Node node) { + String dataSourceName = getDataSourceNameFromNode(node); + String deviceId = getDeviceIdFromNode(node); + correlatedArtifacts.addAll(getArtifactsFromCorrelatableAttributes(node)); + correlatedArtifacts.forEach((eamArtifact) -> { + // get local instances + Collection eamArtifactInstances = getCorrelatedInstances(eamArtifact, dataSourceName, deviceId); + // get global instances + eamArtifactInstances.addAll(getGlobalFileInstancesAsArtifactInstances(eamArtifact)); + + eamArtifactInstances.forEach((eamArtifactInstance) -> { + EamArtifact newCeArtifact = new EamArtifact( + eamArtifact.getArtifactType(), + eamArtifact.getArtifactValue() + ); + newCeArtifact.addInstance(eamArtifactInstance); + tableModel.addEnterpriseArtifactManagerArtifact(newCeArtifact); + }); + }); + + if (correlatedArtifacts.isEmpty()) { + displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_noArtifacts()); + } else if (0 == tableModel.getRowCount()) { + displayMessageOnTableStatusPanel(Bundle.DataContentViewerOtherCases_table_isempty()); + } else { + clearMessageOnTableStatusPanel(); + setColumnWidths(); + } + } + + private void setColumnWidths() { + for (int idx = 0; idx < tableModel.getColumnCount(); idx++) { + TableColumn column = otherCasesTable.getColumnModel().getColumn(idx); + int colWidth = tableModel.getColumnPreferredWidth(idx); + if (0 < colWidth) { + column.setPreferredWidth(colWidth); + } + } + } + + private void displayMessageOnTableStatusPanel(String message) { + tableStatusPanelLabel.setText(message); + tableStatusPanelLabel.setVisible(true); + } + + private void clearMessageOnTableStatusPanel() { + tableStatusPanelLabel.setVisible(false); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + rightClickPopupMenu = new javax.swing.JPopupMenu(); + selectAllMenuItem = new javax.swing.JMenuItem(); + exportToCSVMenuItem = new javax.swing.JMenuItem(); + showCaseDetailsMenuItem = new javax.swing.JMenuItem(); + showCommonalityMenuItem = new javax.swing.JMenuItem(); + CSVFileChooser = new javax.swing.JFileChooser(); + otherCasesPanel = new javax.swing.JPanel(); + tableContainerPanel = new javax.swing.JPanel(); + tableScrollPane = new javax.swing.JScrollPane(); + otherCasesTable = new javax.swing.JTable(); + tableStatusPanel = new javax.swing.JPanel(); + tableStatusPanelLabel = new javax.swing.JLabel(); + + org.openide.awt.Mnemonics.setLocalizedText(selectAllMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.selectAllMenuItem.text")); // NOI18N + rightClickPopupMenu.add(selectAllMenuItem); + + org.openide.awt.Mnemonics.setLocalizedText(exportToCSVMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.exportToCSVMenuItem.text")); // NOI18N + rightClickPopupMenu.add(exportToCSVMenuItem); + + org.openide.awt.Mnemonics.setLocalizedText(showCaseDetailsMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCaseDetailsMenuItem.text")); // NOI18N + rightClickPopupMenu.add(showCaseDetailsMenuItem); + + org.openide.awt.Mnemonics.setLocalizedText(showCommonalityMenuItem, org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.showCommonalityMenuItem.text")); // NOI18N + rightClickPopupMenu.add(showCommonalityMenuItem); + + otherCasesTable.setAutoCreateRowSorter(true); + otherCasesTable.setModel(tableModel); + otherCasesTable.setToolTipText(org.openide.util.NbBundle.getMessage(DataContentViewerOtherCases.class, "DataContentViewerOtherCases.table.toolTip.text")); // NOI18N + otherCasesTable.setComponentPopupMenu(rightClickPopupMenu); + otherCasesTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION); + tableScrollPane.setViewportView(otherCasesTable); + + tableStatusPanelLabel.setForeground(new java.awt.Color(255, 0, 51)); + + javax.swing.GroupLayout tableStatusPanelLayout = new javax.swing.GroupLayout(tableStatusPanel); + tableStatusPanel.setLayout(tableStatusPanelLayout); + tableStatusPanelLayout.setHorizontalGroup( + tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + .addGroup(tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(tableStatusPanelLayout.createSequentialGroup() + .addContainerGap() + .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 780, Short.MAX_VALUE) + .addContainerGap())) + ); + tableStatusPanelLayout.setVerticalGroup( + tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 16, Short.MAX_VALUE) + .addGroup(tableStatusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(tableStatusPanelLayout.createSequentialGroup() + .addComponent(tableStatusPanelLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + ); + + javax.swing.GroupLayout tableContainerPanelLayout = new javax.swing.GroupLayout(tableContainerPanel); + tableContainerPanel.setLayout(tableContainerPanelLayout); + tableContainerPanelLayout.setHorizontalGroup( + tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE) + .addComponent(tableStatusPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + tableContainerPanelLayout.setVerticalGroup( + tableContainerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(tableContainerPanelLayout.createSequentialGroup() + .addComponent(tableScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 371, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(tableStatusPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + + javax.swing.GroupLayout otherCasesPanelLayout = new javax.swing.GroupLayout(otherCasesPanel); + otherCasesPanel.setLayout(otherCasesPanelLayout); + otherCasesPanelLayout.setHorizontalGroup( + otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 800, Short.MAX_VALUE) + .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tableContainerPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + otherCasesPanelLayout.setVerticalGroup( + otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 404, Short.MAX_VALUE) + .addGroup(otherCasesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(otherCasesPanelLayout.createSequentialGroup() + .addComponent(tableContainerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(0, 0, 0))) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(otherCasesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JFileChooser CSVFileChooser; + private javax.swing.JMenuItem exportToCSVMenuItem; + private javax.swing.JPanel otherCasesPanel; + private javax.swing.JTable otherCasesTable; + private javax.swing.JPopupMenu rightClickPopupMenu; + private javax.swing.JMenuItem selectAllMenuItem; + private javax.swing.JMenuItem showCaseDetailsMenuItem; + private javax.swing.JMenuItem showCommonalityMenuItem; + private javax.swing.JPanel tableContainerPanel; + private javax.swing.JScrollPane tableScrollPane; + private javax.swing.JPanel tableStatusPanel; + private javax.swing.JLabel tableStatusPanelLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCasesTableCellRenderer.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCasesTableCellRenderer.java new file mode 100644 index 0000000000..e795de5f32 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCasesTableCellRenderer.java @@ -0,0 +1,71 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.contentviewer; + +import java.awt.Color; +import java.awt.Component; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; + +/** + * Renderer for cells in data content viewer table + */ +public class DataContentViewerOtherCasesTableCellRenderer implements TableCellRenderer { + + public static final DefaultTableCellRenderer DEFAULT_RENDERER = new DefaultTableCellRenderer(); + + @Override + public Component getTableCellRendererComponent( + JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + Component renderer = DEFAULT_RENDERER.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column); + ((JLabel) renderer).setOpaque(true); + Color foreground, background; + if (isSelected) { + foreground = Color.WHITE; + background = Color.BLUE; + } else { + String known_status = (String) table.getModel().getValueAt(row, 5); + switch (known_status) { + case "Bad": + foreground = Color.WHITE; + background = Color.RED; + break; + case "Unknown": + foreground = Color.BLACK; + background = Color.YELLOW; + break; + default: + foreground = Color.BLACK; + background = Color.WHITE; + break; + } + } + renderer.setForeground(foreground); + renderer.setBackground(background); + return renderer; + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCasesTableModel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCasesTableModel.java new file mode 100644 index 0000000000..a6e2a8817b --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/contentviewer/DataContentViewerOtherCasesTableModel.java @@ -0,0 +1,186 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.contentviewer; + +import java.util.ArrayList; +import java.util.List; +import javax.swing.table.AbstractTableModel; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactInstance; + +/** + * Model for cells in data content viewer table + */ +public class DataContentViewerOtherCasesTableModel extends AbstractTableModel { + + @Messages({"DataContentViewerOtherCasesTableModel.case=Case", + "DataContentViewerOtherCasesTableModel.device=Device", + "DataContentViewerOtherCasesTableModel.dataSource=Data Source", + "DataContentViewerOtherCasesTableModel.path=Path", + "DataContentViewerOtherCasesTableModel.type=Correlation Type", + "DataContentViewerOtherCasesTableModel.value=Correlation Value", + "DataContentViewerOtherCasesTableModel.scope=Scope", // was globalStatus + "DataContentViewerOtherCasesTableModel.known=Known", // was knownStatus + "DataContentViewerOtherCasesTableModel.comment=Comment", + "DataContentViewerOtherCasesTableModel.noData=No Data.",}) + private enum TableColumns { + // Ordering here determines displayed column order in Content Viewer. + // If order is changed, update the CellRenderer to ensure correct row coloring. + CASE_NAME(Bundle.DataContentViewerOtherCasesTableModel_case(), 75), + DATA_SOURCE(Bundle.DataContentViewerOtherCasesTableModel_dataSource(), 75), + DEVICE(Bundle.DataContentViewerOtherCasesTableModel_device(), 145), + TYPE(Bundle.DataContentViewerOtherCasesTableModel_type(), 40), + VALUE(Bundle.DataContentViewerOtherCasesTableModel_value(), 145), + KNOWN(Bundle.DataContentViewerOtherCasesTableModel_known(), 25), + SCOPE(Bundle.DataContentViewerOtherCasesTableModel_scope(), 20), + COMMENT(Bundle.DataContentViewerOtherCasesTableModel_comment(), 200), + FILE_PATH(Bundle.DataContentViewerOtherCasesTableModel_path(), 250); + + private final String columnName; + private final int columnWidth; + + TableColumns(String columnName, int columnWidth) { + this.columnName = columnName; + this.columnWidth = columnWidth; + } + + public String columnName() { + return columnName; + } + + public int columnWidth() { + return columnWidth; + } + }; + + List eamArtifacts; + + DataContentViewerOtherCasesTableModel() { + eamArtifacts = new ArrayList<>(); + } + + @Override + public int getColumnCount() { + return TableColumns.values().length; + } + + /** + * Get the preferred width that has been configured for this column. + * + * A value of 0 means that no preferred width has been defined for this + * column. + * + * @param colIdx Column index + * + * @return preferred column width >= 0 + */ + public int getColumnPreferredWidth(int colIdx) { + return TableColumns.values()[colIdx].columnWidth(); + } + + @Override + public int getRowCount() { + return eamArtifacts.size(); + } + + @Override + public String getColumnName(int colIdx) { + return TableColumns.values()[colIdx].columnName(); + } + + @Override + public Object getValueAt(int rowIdx, int colIdx) { + if (0 == eamArtifacts.size()) { + return Bundle.DataContentViewerOtherCasesTableModel_noData(); + } + + return mapValueById(rowIdx, TableColumns.values()[colIdx]); + } + + public Object getRow(int rowIdx) { + return eamArtifacts.get(rowIdx); + } + + /** + * Map a rowIdx and colId to the value in that cell. + * + * @param rowIdx Index of row to search + * @param colId ID of column to search + * + * @return value in the cell + */ + private Object mapValueById(int rowIdx, TableColumns colId) { + EamArtifact eamArtifact = eamArtifacts.get(rowIdx); + EamArtifactInstance eamArtifactInstance = eamArtifact.getInstances().get(0); + String value = Bundle.DataContentViewerOtherCasesTableModel_noData(); + + switch (colId) { + case CASE_NAME: + value = eamArtifactInstance.getEamCase().getDisplayName(); + break; + case DEVICE: + value = eamArtifactInstance.getEamDataSource().getDeviceID(); + break; + case DATA_SOURCE: + value = eamArtifactInstance.getEamDataSource().getName(); + break; + case FILE_PATH: + value = eamArtifactInstance.getFilePath(); + break; + case TYPE: + value = eamArtifact.getArtifactType().getName(); + break; + case VALUE: + value = eamArtifact.getArtifactValue(); + break; + case SCOPE: + value = eamArtifactInstance.getGlobalStatus().toString(); + break; + case KNOWN: + value = eamArtifactInstance.getKnownStatus().toString(); + break; + case COMMENT: + value = eamArtifactInstance.getComment(); + break; + } + return value; + } + + @Override + public Class getColumnClass(int colIdx) { + return String.class; + } + + /** + * Add one local Enterprise Artifact Manager Artifact to the table. + * + * @param eamArtifact Enterprise Artifact Manager Artifact to add to the + * table + */ + public void addEnterpriseArtifactManagerArtifact(EamArtifact eamArtifact) { + eamArtifacts.add(eamArtifact); + fireTableDataChanged(); + } + + public void clearTable() { + eamArtifacts.clear(); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/AbstractSqlEamDb.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/AbstractSqlEamDb.java new file mode 100644 index 0000000000..07e579b43f --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/AbstractSqlEamDb.java @@ -0,0 +1,1988 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; + +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * + * SQLite manager implementation + * + */ +public abstract class AbstractSqlEamDb implements EamDb { + + private final static Logger LOGGER = Logger.getLogger(AbstractSqlEamDb.class.getName()); + + protected static final int SCHEMA_VERSION = 1; + protected final List DEFAULT_ARTIFACT_TYPES = new ArrayList<>(); + + private int bulkArtifactsCount; + private int bulkGlobalArtifactsCount; + protected int bulkArtifactsThreshold; + private final Map> bulkArtifacts; + private final Map> bulkGlobalArtifacts; + private final List badTags; + + /** + * Connect to the DB and initialize it. + * + * @throws UnknownHostException, EamDbException + */ + protected AbstractSqlEamDb() { + badTags = new ArrayList(); + bulkArtifactsCount = 0; + bulkGlobalArtifactsCount = 0; + bulkArtifacts = new HashMap<>(); + bulkGlobalArtifacts = new HashMap<>(); + + loadDefaultArtifactTypes(); + + for (EamArtifact.Type type : DEFAULT_ARTIFACT_TYPES) { + bulkArtifacts.put(type.getName(), new ArrayList<>()); + bulkGlobalArtifacts.put(type.getName(), new ArrayList<>()); + } + } + + /** + * Load the default correlation artifact types + */ + private void loadDefaultArtifactTypes() { + DEFAULT_ARTIFACT_TYPES.add(new EamArtifact.Type("FILES", true, true)); // NON-NLS + DEFAULT_ARTIFACT_TYPES.add(new EamArtifact.Type("DOMAIN", true, false)); // NON-NLS + DEFAULT_ARTIFACT_TYPES.add(new EamArtifact.Type("EMAIL", true, false)); // NON-NLS + DEFAULT_ARTIFACT_TYPES.add(new EamArtifact.Type("PHONE", true, false)); // NON-NLS + DEFAULT_ARTIFACT_TYPES.add(new EamArtifact.Type("USBID", true, false)); // NON-NLS + } + + /** + * Store the default Correlation Artifact Types in the db. + * + * This should be called immediately following the database schema being + * loaded. + * + * @throws EamDbException + */ + private void storeDefaultArtifactTypes() throws EamDbException { + for (EamArtifact.Type defType : DEFAULT_ARTIFACT_TYPES) { + newCorrelationArtifactType(defType); + } + } + + /** + * Store the schema version into the db_info table. + * + * This should be called immediately following the database schema being + * loaded. + * + * @throws EamDbException + */ + private void storeSchemaVersion() throws EamDbException { + newDbInfo("SCHEMA_VERSION", String.valueOf(SCHEMA_VERSION)); + } + + /** + * Insert all default content into a freshly initialized/reset database. + * + * This should be called immediately following the database schema being + * reset or initialized. + * + * @throws EamDbException + */ + protected void insertDefaultContent() throws EamDbException { + storeDefaultArtifactTypes(); + storeSchemaVersion(); + } + + /** + * Check to see if the database schema exists and is the current version. - + * If it doesn't exist, initialize it and load default content. - If it is + * not the current version, update it. - If it is already initialized and is + * the current version, do nothing. + * + * Note: this should be call after the connectionPool is initialized. + */ + protected void confirmDatabaseSchema() throws EamDbException { + int schema_version; + try { + schema_version = Integer.parseInt(getDbInfo("SCHEMA_VERSION")); + } catch (EamDbException | NumberFormatException ex) { + // error likely means we have not initialized the schema + schema_version = 0; + LOGGER.log(Level.WARNING, "Could not find SCHEMA_VERSION in db_info table, assuming database is not initialized.", ex); // NON-NLS + } + + if (0 == schema_version) { + initializeDatabaseSchema(); + insertDefaultContent(); + } else if (SCHEMA_VERSION > schema_version) { + // FUTURE: upgrade schema + } + // else, schema is current + } + + /** + * Create the schema for the selected database implementation + */ + protected abstract void initializeDatabaseSchema() throws EamDbException; + + /** + * Setup and create a connection to the selected database implementation + */ + protected abstract Connection connect() throws EamDbException; + + /** + * Close the in-use connection and return it to the pool. + * + * @param conn An open connection + * + * @throws EamDbException + */ + protected void closeConnection(Connection conn) throws EamDbException { + if (null != conn) { + try { + conn.close(); + } catch (SQLException ex) { + throw new EamDbException("Error closing Connection.", ex); + } + } + } + + /** + * Close the prepared statement. + * + * @param preparedStatement + * + * @throws EamDbException + */ + protected void closePreparedStatement(PreparedStatement preparedStatement) throws EamDbException { + if (null != preparedStatement) { + try { + preparedStatement.close(); + } catch (SQLException ex) { + throw new EamDbException("Error closing PreparedStatement.", ex); + } + } + } + + /** + * Close the resultSet. + * + * @param resultSet + * + * @throws EamDbException + */ + protected void closeResultSet(ResultSet resultSet) throws EamDbException { + if (null != resultSet) { + try { + resultSet.close(); + } catch (SQLException ex) { + throw new EamDbException("Error closing ResultSet.", ex); + } + } + } + + /** + * Get the list of tags recognized as "Bad" + * + * @return The list of bad tags + */ + @Override + public List getBadTags() { + synchronized (badTags) { + return new ArrayList<>(badTags); + } + } + + /** + * Set the tags recognized as "Bad" + * + * @param tags The tags to consider bad + */ + @Override + public void setBadTags(List tags) { + synchronized (badTags) { + badTags.clear(); + badTags.addAll(tags); + } + } + + /** + * Add a new name/value pair in the db_info table. + * + * @param name Key to set + * @param value Value to set + * + * @throws EamDbException + */ + @Override + public void newDbInfo(String name, String value) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + String sql = "INSERT INTO db_info (name, value) VALUES (?, ?)"; + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, name); + preparedStatement.setString(2, value); + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error adding new name/value pair to db_info.", ex); + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + + } + + /** + * Get the value for the given name from the name/value db_info table. + * + * @param name Name to search for + * + * @return value associated with name. + * + * @throws EamDbException + */ + @Override + public String getDbInfo(String name) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String value = null; + String sql = "SELECT value FROM db_info WHERE name=?"; + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, name); + resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + value = resultSet.getString("value"); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting value for name.", ex); + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return value; + } + + /** + * Update the value for a name in the name/value db_info table. + * + * @param name Name to find + * @param value Value to assign to name. + * + * @throws EamDbException + */ + @Override + public void updateDbInfo(String name, String value) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + String sql = "UPDATE db_info SET value=? WHERE name=?"; + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, value); + preparedStatement.setString(2, name); + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error updating value for name.", ex); + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Creates new Case in the database + * + * Expects the Organization for this case to already exist in the database. + * + * @param eamCase The case to add + */ + @Override + public void newCase(EamCase eamCase) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + + String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, " + + "examiner_name, examiner_email, examiner_phone, notes) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + try { + preparedStatement = conn.prepareStatement(sql); + + preparedStatement.setString(1, eamCase.getCaseUUID()); + if (null == eamCase.getOrg()) { + preparedStatement.setNull(2, Types.INTEGER); + } else { + preparedStatement.setInt(2, eamCase.getOrg().getOrgID()); + } + preparedStatement.setString(3, eamCase.getDisplayName()); + preparedStatement.setString(4, eamCase.getCreationDate()); + preparedStatement.setString(5, eamCase.getCaseNumber()); + preparedStatement.setString(6, eamCase.getExaminerName()); + preparedStatement.setString(7, eamCase.getExaminerEmail()); + preparedStatement.setString(8, eamCase.getExaminerPhone()); + preparedStatement.setString(9, eamCase.getNotes()); + + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error inserting new case.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Updates an existing Case in the database + * + * @param eamCase The case to update + */ + @Override + public void updateCase(EamCase eamCase) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + String sql = "UPDATE cases " + + "SET org_id=?, case_name=?, creation_date=?, case_number=?, examiner_name=?, examiner_email=?, examiner_phone=?, notes=? " + + "WHERE case_uid=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + + if (null == eamCase.getOrg()) { + preparedStatement.setNull(1, Types.INTEGER); + } else { + preparedStatement.setInt(1, eamCase.getOrg().getOrgID()); + } + preparedStatement.setString(2, eamCase.getDisplayName()); + preparedStatement.setString(3, eamCase.getCreationDate()); + preparedStatement.setString(4, eamCase.getCaseNumber()); + preparedStatement.setString(5, eamCase.getExaminerName()); + preparedStatement.setString(6, eamCase.getExaminerEmail()); + preparedStatement.setString(7, eamCase.getExaminerPhone()); + preparedStatement.setString(8, eamCase.getNotes()); + preparedStatement.setString(9, eamCase.getCaseUUID()); + + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error updating case.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Retrieves Case details based on Case UUID + * + * @param caseUUID unique identifier for a case + * + * @return The retrieved case + */ + @Override + public EamCase getCaseDetails(String caseUUID) throws EamDbException { + Connection conn = connect(); + + EamCase eamCaseResult = null; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, " + + "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone " + + "FROM cases " + + "LEFT JOIN organizations ON cases.org_id=organizations.id " + + "WHERE case_uid=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, caseUUID); + resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + eamCaseResult = getEnterpriseArtifactManagerCaseFromResultSet(resultSet); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting case details.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return eamCaseResult; + } + + /** + * Retrieves cases that are in DB. + * + * @return List of cases + */ + @Override + public List getCases() throws EamDbException { + Connection conn = connect(); + + List cases = new ArrayList<>(); + EamCase eamCaseResult; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String sql = "SELECT cases.id as case_id, case_uid, case_name, creation_date, case_number, examiner_name, " + + "examiner_email, examiner_phone, notes, organizations.id as org_id, org_name, poc_name, poc_email, poc_phone " + + "FROM cases " + + "LEFT JOIN organizations ON cases.org_id=organizations.id"; + + try { + preparedStatement = conn.prepareStatement(sql); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + eamCaseResult = getEnterpriseArtifactManagerCaseFromResultSet(resultSet); + cases.add(eamCaseResult); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting all cases.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return cases; + } + + /** + * Creates new Data Source in the database + * + * @param eamDataSource the data source to add + */ + @Override + public void newDataSource(EamDataSource eamDataSource) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + + String sql = "INSERT INTO data_sources(device_id, name) VALUES (?, ?)"; + + try { + preparedStatement = conn.prepareStatement(sql); + + preparedStatement.setString(1, eamDataSource.getDeviceID()); + preparedStatement.setString(2, eamDataSource.getName()); + + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error inserting new data source.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Updates a Data Source in the database + * + * @param eamDataSource the data source to update + */ + @Override + public void updateDataSource(EamDataSource eamDataSource) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + String sql = "UPDATE data_sources SET name=? WHERE device_id=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + + preparedStatement.setString(1, eamDataSource.getName()); + preparedStatement.setString(2, eamDataSource.getDeviceID()); + + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error updating case.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Retrieves Data Source details based on data source device ID + * + * @param dataSourceDeviceId the data source device ID number + * + * @return The data source + */ + @Override + public EamDataSource getDataSourceDetails(String dataSourceDeviceId) throws EamDbException { + Connection conn = connect(); + + EamDataSource eamDataSourceResult = null; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String sql = "SELECT * FROM data_sources WHERE device_id=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, dataSourceDeviceId); + resultSet = preparedStatement.executeQuery(); + if (resultSet.next()) { + eamDataSourceResult = getEnterpriseArtifactManagerDataSourceFromResultSet(resultSet); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting case details.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return eamDataSourceResult; + } + + /** + * Return a list of data sources in the DB + * + * @return list of data sources in the DB + */ + @Override + public List getDataSources() throws EamDbException { + Connection conn = connect(); + + List dataSources = new ArrayList<>(); + EamDataSource eamDataSourceResult; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String sql = "SELECT * FROM data_sources"; + + try { + preparedStatement = conn.prepareStatement(sql); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + eamDataSourceResult = getEnterpriseArtifactManagerDataSourceFromResultSet(resultSet); + dataSources.add(eamDataSourceResult); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting all data sources.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return dataSources; + } + + private String artifactTypeToTableName(EamArtifact.Type type) { + return type.getName() + "_instances"; + } + + /** + * Inserts new Artifact(s) into the database. Should add associated Case and + * Data Source first. + * + * @param eamArtifact The artifact to add + */ + @Override + public void addArtifact(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + List eamInstances = eamArtifact.getInstances(); + PreparedStatement preparedStatement = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + StringBuilder sql = new StringBuilder(); + sql.append("INSERT INTO "); + sql.append(tableName); + sql.append("(case_id, data_source_id, value, file_path, known_status, comment) "); + sql.append("VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "); + sql.append("(SELECT id FROM data_sources WHERE device_id=? LIMIT 1), ?, ?, ?, ?)"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + for (EamArtifactInstance eamInstance : eamInstances) { + preparedStatement.setString(1, eamInstance.getEamCase().getCaseUUID()); + preparedStatement.setString(2, eamInstance.getEamDataSource().getDeviceID()); + preparedStatement.setString(3, eamArtifact.getArtifactValue()); + preparedStatement.setString(4, eamInstance.getFilePath()); + preparedStatement.setString(5, eamInstance.getKnownStatus().name()); + preparedStatement.setString(6, eamInstance.getComment()); + + preparedStatement.executeUpdate(); + } + } catch (SQLException ex) { + throw new EamDbException("Error inserting new artifact into artifacts table.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Retrieves eamArtifact instances from the database that are associated with + * the eamArtifactType and eamArtifactValue of the given eamArtifact. + * + * @param eamArtifact The type/value to look up (artifact with 0 instances) + * + * @return List of artifact instances for a given type/value + */ + @Override + public List getArtifactInstancesByTypeValue(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + List artifactInstances = new ArrayList<>(); + + EamArtifactInstance artifactInstance; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment FROM "); + sql.append(tableName); + sql.append(" LEFT JOIN cases ON "); + sql.append(tableName); + sql.append(".case_id=cases.id"); + sql.append(" LEFT JOIN data_sources ON "); + sql.append(tableName); + sql.append(".data_source_id=data_sources.id"); + sql.append(" WHERE value=?"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, eamArtifact.getArtifactValue()); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + artifactInstance = getEnterpriseArtifactManagerArtifactInstanceFromResultSet(resultSet); + artifactInstances.add(artifactInstance); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting artifact instances by artifactType and artifactValue.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return artifactInstances; + } + + /** + * Retrieves eamArtifact instances from the database that are associated with + * the aType and filePath + * + * @param aType EamArtifact.Type to search for + * @param filePath File path to search for + * + * @return List of 0 or more EnterpriseArtifactManagerArtifactInstances + * + * @throws EamDbException + */ + @Override + public List getArtifactInstancesByPath(EamArtifact.Type aType, String filePath) throws EamDbException { + Connection conn = connect(); + + List artifactInstances = new ArrayList<>(); + + EamArtifactInstance artifactInstance; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(aType); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment FROM "); + sql.append(tableName); + sql.append(" LEFT JOIN cases ON "); + sql.append(tableName); + sql.append(".case_id=cases.id"); + sql.append(" LEFT JOIN data_sources ON "); + sql.append(tableName); + sql.append(".data_source_id=data_sources.id"); + sql.append(" WHERE file_path=?"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, filePath); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + artifactInstance = getEnterpriseArtifactManagerArtifactInstanceFromResultSet(resultSet); + artifactInstances.add(artifactInstance); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting artifact instances by artifactType and artifactValue.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return artifactInstances; + } + + /** + * Retrieves number of artifact instances in the database that are + * associated with the ArtifactType and artifactValue of the given artifact. + * + * @param eamArtifact Artifact with artifactType and artifactValue to search + * for + * + * @return Number of artifact instances having ArtifactType and + * ArtifactValue. + */ + @Override + public Long getCountArtifactInstancesByTypeValue(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + Long instanceCount = 0L; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT count(*) FROM "); + sql.append(tableName); + sql.append(" WHERE value=?"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, eamArtifact.getArtifactValue()); + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + instanceCount = resultSet.getLong(1); + } catch (SQLException ex) { + throw new EamDbException("Error getting count of artifact instances by artifactType and artifactValue.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return instanceCount; + } + + /** + * Using the ArtifactType and ArtifactValue from the given eamArtfact, + * compute the ratio of: (The number of unique case_id/datasource_id tuples + * where Type/Value is found) divided by (The total number of unique + * case_id/datasource_id tuples in the database) expressed as a percentage. + * + * @param eamArtifact Artifact with artifactType and artifactValue to search + * for + * + * @return Int between 0 and 100 + */ + @Override + public int getCommonalityPercentageForTypeValue(EamArtifact eamArtifact) throws EamDbException { + Double uniqueTypeValueTuples = getCountUniqueCaseDataSourceTuplesHavingTypeValue(eamArtifact).doubleValue(); + Double uniqueCaseDataSourceTuples = getCountUniqueCaseDataSourceTuples().doubleValue(); + Double commonalityPercentage = uniqueTypeValueTuples / uniqueCaseDataSourceTuples * 100; + return commonalityPercentage.intValue(); + } + + /** + * Retrieves number of unique caseDisplayName / dataSource tuples in the + * database that are associated with the artifactType and artifactValue of + * the given artifact. + * + * @param eamArtifact Artifact with artifactType and artifactValue to search + * for + * + * @return Number of unique tuples + */ + @Override + public Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + Long instanceCount = 0L; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT count(*) FROM (SELECT DISTINCT case_id, data_source_id FROM "); + sql.append(tableName); + sql.append(" WHERE value=?) AS "); + sql.append(tableName); + sql.append("_distinct_case_data_source_tuple"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, eamArtifact.getArtifactValue()); + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + instanceCount = resultSet.getLong(1); + } catch (SQLException ex) { + throw new EamDbException("Error counting unique caseDisplayName/dataSource tuples having artifactType and artifactValue.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return instanceCount; + } + + /** + * Retrieves number of unique caseDisplayName/dataSource tuples in the + * database. + * + * @return Number of unique tuples + */ + @Override + public Long getCountUniqueCaseDataSourceTuples() throws EamDbException { + Connection conn = connect(); + + Long instanceCount = 0L; + List artifactTypes = getCorrelationArtifactTypes(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT 0 "); + + for (EamArtifact.Type type : artifactTypes) { + // TODO: when/if custom types are allowed, make this more safe. + String table_name = type.getName() + "_instances"; + + sql.append("+ (SELECT count(*) FROM (SELECT DISTINCT case_id, data_source_id FROM "); + sql.append(table_name); + sql.append(") AS "); + sql.append(table_name); + sql.append("_distinct_case_data_source_tuple) "); + } + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + instanceCount = resultSet.getLong(1); + } catch (SQLException ex) { + throw new EamDbException("Error counting unique caseDisplayName/dataSource tuples.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return instanceCount; + } + + /** + * Retrieves number of eamArtifact instances in the database that are + * associated with the caseDisplayName and dataSource of the given + * eamArtifact instance. + * + * @param eamInstance Instance with caseName and dataSource to search for + * + * @param eamInstance Instance with caseDisplayName and dataSource to search + * for + * + * @return Number of artifact instances having caseDisplayName and + * dataSource + */ + @Override + public Long getCountArtifactInstancesByCaseDataSource(EamArtifactInstance eamInstance) throws EamDbException { + Connection conn = connect(); + + Long instanceCount = 0L; + List artifactTypes = getCorrelationArtifactTypes(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + // Figure out sql variables or subqueries + StringBuilder sql = new StringBuilder(); + sql.append("SELECT 0 "); + + for (EamArtifact.Type type : artifactTypes) { + String table_name = type.getName() + "_instances"; + + sql.append("+ (SELECT count(*) FROM "); + sql.append(table_name); + sql.append(" WHERE case_id=(SELECT id FROM cases WHERE case_uid=?))");// and data_source_id=(SELECT id FROM data_sources WHERE name=?))"); + } + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + + for (int i = 0; i < artifactTypes.size(); ++i) { + preparedStatement.setString(i + 1, eamInstance.getEamCase().getCaseUUID()); + } + + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + instanceCount = resultSet.getLong(1); + } catch (SQLException ex) { + throw new EamDbException("Error counting artifact instances by caseName/dataSource.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return instanceCount; + } + + /** + * Adds an eamArtifact to an internal list to be later added to DB. Artifact + * can have 1 or more Artifact Instances. Insert will be triggered by a + * threshold or a call to bulkInsertArtifacts(). + * + * @param eamArtifact The artifact to add + */ + @Override + public void prepareBulkArtifact(EamArtifact eamArtifact) throws EamDbException { + + synchronized (bulkArtifacts) { + bulkArtifacts.get(eamArtifact.getArtifactType().getName()).add(eamArtifact); + bulkArtifactsCount++; + + if (bulkArtifactsCount >= bulkArtifactsThreshold) { + bulkInsertArtifacts(); + } + } + } + + /** + * Executes a bulk insert of the eamArtifacts added from the + * prepareBulkArtifact() method + */ + @Override + public void bulkInsertArtifacts() throws EamDbException { + List artifactTypes = getCorrelationArtifactTypes(); + + Connection conn = connect(); + PreparedStatement bulkPs = null; + + try { + synchronized (bulkArtifacts) { + if (bulkArtifactsCount == 0) { + return; + } + + for (EamArtifact.Type type : artifactTypes) { + + String tableName = artifactTypeToTableName(type); + StringBuilder sql = new StringBuilder(); + sql.append("INSERT INTO "); + sql.append(tableName); + sql.append(" (case_id, data_source_id, value, file_path, known_status, comment) "); + sql.append("VALUES ((SELECT id FROM cases WHERE case_uid=? LIMIT 1), "); + sql.append("(SELECT id FROM data_sources WHERE name=? LIMIT 1), ?, ?, ?, ?)"); + + bulkPs = conn.prepareStatement(sql.toString()); + + Collection eamArtifacts = bulkArtifacts.get(type.getName()); + for (EamArtifact eamArtifact : eamArtifacts) { + List eamInstances = eamArtifact.getInstances(); + + for (EamArtifactInstance eamInstance : eamInstances) { + bulkPs.setString(1, eamInstance.getEamCase().getCaseUUID()); + bulkPs.setString(2, eamInstance.getEamDataSource().getName()); + bulkPs.setString(3, eamArtifact.getArtifactValue()); + bulkPs.setString(4, eamInstance.getFilePath()); + bulkPs.setString(5, eamInstance.getKnownStatus().name()); + bulkPs.setString(6, eamInstance.getComment()); + bulkPs.addBatch(); + } + } + + bulkPs.executeBatch(); + bulkArtifacts.get(type.getName()).clear(); + } + + // Reset state + bulkArtifactsCount = 0; + } + } catch (SQLException ex) { + throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS + } finally { + closePreparedStatement(bulkPs); + closeConnection(conn); + } + } + + /** + * Executes a bulk insert of the cases + */ + @Override + public void bulkInsertCases(List cases) throws EamDbException { + Connection conn = connect(); + + if (cases.isEmpty()) { + return; + } + + int counter = 0; + PreparedStatement bulkPs = null; + try { + String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, " + + "examiner_name, examiner_email, examiner_phone, notes) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + bulkPs = conn.prepareStatement(sql); + + for (EamCase eamCase : cases) { + bulkPs.setString(1, eamCase.getCaseUUID()); + if (null == eamCase.getOrg()) { + bulkPs.setNull(2, Types.INTEGER); + } else { + bulkPs.setInt(2, eamCase.getOrg().getOrgID()); + } + bulkPs.setString(3, eamCase.getDisplayName()); + bulkPs.setString(4, eamCase.getCreationDate()); + bulkPs.setString(5, eamCase.getCaseNumber()); + bulkPs.setString(6, eamCase.getExaminerName()); + bulkPs.setString(7, eamCase.getExaminerEmail()); + bulkPs.setString(8, eamCase.getExaminerPhone()); + bulkPs.setString(9, eamCase.getNotes()); + bulkPs.addBatch(); + + counter++; + + // limit a batch's max size to bulkArtifactsThreshold + if (counter >= bulkArtifactsThreshold) { + bulkPs.executeBatch(); + counter = 0; + } + } + // send the remaining batch records + bulkPs.executeBatch(); + } catch (SQLException ex) { + throw new EamDbException("Error inserting bulk cases.", ex); // NON-NLS + } finally { + closePreparedStatement(bulkPs); + closeConnection(conn); + } + } + + /** + * Sets an eamArtifact instance as knownStatus = "Bad". If eamArtifact exists, + * it is updated. If eamArtifact does not exist nothing happens + * + * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. + */ + @Override + public void setArtifactInstanceKnownBad(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + if (1 != eamArtifact.getInstances().size()) { + throw new EamDbException("Error: Artifact must have exactly one (1) Artifact Instance to set known bad."); // NON-NLS + } + + List eamInstances = eamArtifact.getInstances(); + EamArtifactInstance eamInstance = eamInstances.get(0); + + PreparedStatement preparedUpdate = null; + PreparedStatement preparedQuery = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + + StringBuilder sqlQuery = new StringBuilder(); + sqlQuery.append("SELECT id FROM "); + sqlQuery.append(tableName); + sqlQuery.append(" WHERE case_id=(SELECT id FROM cases WHERE case_uid=?) "); + sqlQuery.append("AND data_source_id=(SELECT id FROM data_sources WHERE device_id=?) "); + sqlQuery.append("AND value=? "); + sqlQuery.append("AND file_path=?"); + + StringBuilder sqlUpdate = new StringBuilder(); + sqlUpdate.append("UPDATE "); + sqlUpdate.append(tableName); + sqlUpdate.append(" SET known_status=?, comment=? "); + sqlUpdate.append("WHERE id=?"); + + try { + preparedQuery = conn.prepareStatement(sqlQuery.toString()); + preparedQuery.setString(1, eamInstance.getEamCase().getCaseUUID()); + preparedQuery.setString(2, eamInstance.getEamDataSource().getDeviceID()); + preparedQuery.setString(3, eamArtifact.getArtifactValue()); + preparedQuery.setString(4, eamInstance.getFilePath()); + resultSet = preparedQuery.executeQuery(); + if (resultSet.next()) { + int instance_id = resultSet.getInt("id"); + preparedUpdate = conn.prepareStatement(sqlUpdate.toString()); + + preparedUpdate.setString(1, EamArtifactInstance.KnownStatus.BAD.name()); + preparedUpdate.setString(2, eamInstance.getComment()); + preparedUpdate.setInt(3, instance_id); + + preparedUpdate.executeUpdate(); + } else { + eamArtifact.getInstances().get(0).setKnownStatus(EamArtifactInstance.KnownStatus.BAD); + addArtifact(eamArtifact); + } + + } catch (SQLException ex) { + throw new EamDbException("Error getting/setting artifact instance knownStatus=Bad.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedUpdate); + closePreparedStatement(preparedQuery); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Gets list of matching eamArtifact instances that have knownStatus = "Bad". + * + * @param eamArtifact Artifact containing Type and Value + * + * @return List with 0 or more matching eamArtifact instances. + */ + @Override + public List getArtifactInstancesKnownBad(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + List artifactInstances = new ArrayList<>(); + + EamArtifactInstance artifactInstance; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT cases.case_name, cases.case_uid, data_sources.name, device_id, file_path, known_status, comment FROM "); + sql.append(tableName); + sql.append(" LEFT JOIN cases ON "); + sql.append(tableName); + sql.append(".case_id=cases.id"); + sql.append(" LEFT JOIN data_sources ON "); + sql.append(tableName); + sql.append(".data_source_id=data_sources.id"); + sql.append(" WHERE value=? AND known_status=?"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, eamArtifact.getArtifactValue()); + preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + artifactInstance = getEnterpriseArtifactManagerArtifactInstanceFromResultSet(resultSet); + artifactInstances.add(artifactInstance); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting known bad artifact instances.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return artifactInstances; + } + + /** + * Count matching eamArtifacts instances that have knownStatus = "Bad". + * + * @param eamArtifact Artifact containing Type and Value + * + * @return Number of matching eamArtifacts + */ + @Override + public Long getCountArtifactInstancesKnownBad(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + Long badInstances = 0L; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT count(*) FROM "); + sql.append(tableName); + sql.append(" WHERE value=? AND known_status=?"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, eamArtifact.getArtifactValue()); + preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + badInstances = resultSet.getLong(1); + } catch (SQLException ex) { + throw new EamDbException("Error getting count of known bad artifact instances.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return badInstances; + } + + /** + * Gets list of distinct case display names, where each case has 1+ Artifact + * Instance matching eamArtifact with knownStatus = "Bad". + * + * @param eamArtifact Artifact containing Type and Value + * + * @return List of cases containing this artifact with instances marked as + * bad + * + * @throws EamDbException + */ + @Override + public List getListCasesHavingArtifactInstancesKnownBad(EamArtifact eamArtifact) throws EamDbException { + Connection conn = connect(); + + Collection caseNames = new LinkedHashSet<>(); + + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + + String tableName = artifactTypeToTableName(eamArtifact.getArtifactType()); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT DISTINCT case_name FROM "); + sql.append(tableName); + sql.append(" INNER JOIN cases ON "); + sql.append(tableName); + sql.append(".case_id=cases.id WHERE "); + sql.append(tableName); + sql.append(".value=? AND "); + sql.append(tableName); + sql.append(".known_status=?"); + + try { + preparedStatement = conn.prepareStatement(sql.toString()); + preparedStatement.setString(1, eamArtifact.getArtifactValue()); + preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + caseNames.add(resultSet.getString("case_name")); + } + } catch (SQLException ex) { + throw new EamDbException("Error getting known bad artifact instances.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return caseNames.stream().collect(Collectors.toList()); + } + + /** + * Is the artifact globally known as bad? + * + * @param eamArtifact Artifact containing Type and Value + * + * @return Global known status of the artifact + */ + @Override + public boolean isArtifactGlobalKnownBad(EamArtifact eamArtifact) throws EamDbException { + + // TEMP: Only support file types + if (eamArtifact.getArtifactType() != getCorrelationArtifactTypeByName("FILES")) { + return false; + } + + Connection conn = connect(); + + Long badInstances = 0L; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String sql = "SELECT count(*) FROM global_files WHERE value=? AND known_status=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, eamArtifact.getArtifactValue()); + preparedStatement.setString(2, EamArtifactInstance.KnownStatus.BAD.name()); + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + badInstances = resultSet.getLong(1); + } catch (SQLException ex) { + throw new EamDbException("Error determining if artifact is globally known bad.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + + return 0 < badInstances; + } + + /** + * Add a new organization + * + * @param eamOrg The organization to add + * + * @throws EamDbException + */ + @Override + public void newOrganization(EamOrganization eamOrg) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + String sql = "INSERT INTO organizations(org_name, poc_name, poc_email, poc_phone) VALUES (?, ?, ?, ?)"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, eamOrg.getName()); + preparedStatement.setString(2, eamOrg.getPocName()); + preparedStatement.setString(3, eamOrg.getPocEmail()); + preparedStatement.setString(4, eamOrg.getPocPhone()); + + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error inserting new organization.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Get all organizations + * + * @return A list of all organizations + * + * @throws EamDbException + */ + @Override + public List getOrganizations() throws EamDbException { + Connection conn = connect(); + + List orgs = new ArrayList<>(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String sql = "SELECT * FROM organizations"; + + try { + preparedStatement = conn.prepareStatement(sql); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + orgs.add(getEnterpriseArtifactManagerOrganizationFromResultSet(resultSet)); + } + return orgs; + + } catch (SQLException ex) { + throw new EamDbException("Error getting all organizations.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Get an organization having the given ID + * + * @param orgID The id to look up + * + * @return The organization with the given ID + * + * @throws EamDbException + */ + @Override + public EamOrganization getOrganizationByID(int orgID) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String sql = "SELECT * FROM organizations WHERE id=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setInt(1, orgID); + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + return getEnterpriseArtifactManagerOrganizationFromResultSet(resultSet); + + } catch (SQLException ex) { + throw new EamDbException("Error getting organization by id.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Add a new Global Set + * + * @param eamGlobalSet The global set to add + * + * @return The ID of the new global set + * + * @throws EamDbException + */ + @Override + public int newGlobalSet(EamGlobalSet eamGlobalSet) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement1 = null; + PreparedStatement preparedStatement2 = null; + ResultSet resultSet = null; + String sql1 = "INSERT INTO global_reference_sets(org_id, set_name, version, import_date) VALUES (?, ?, ?, ?)"; + String sql2 = "SELECT id FROM global_reference_sets WHERE org_id=? AND set_name=? AND version=? AND import_date=? LIMIT 1"; + + try { + preparedStatement1 = conn.prepareStatement(sql1); + preparedStatement1.setInt(1, eamGlobalSet.getOrgID()); + preparedStatement1.setString(2, eamGlobalSet.getSetName()); + preparedStatement1.setString(3, eamGlobalSet.getVersion()); + preparedStatement1.setString(4, eamGlobalSet.getImportDate().toString()); + + preparedStatement1.executeUpdate(); + + preparedStatement2 = conn.prepareStatement(sql2); + preparedStatement2.setInt(1, eamGlobalSet.getOrgID()); + preparedStatement2.setString(2, eamGlobalSet.getSetName()); + preparedStatement2.setString(3, eamGlobalSet.getVersion()); + preparedStatement2.setString(4, eamGlobalSet.getImportDate().toString()); + + resultSet = preparedStatement2.executeQuery(); + resultSet.next(); + return resultSet.getInt("id"); + + } catch (SQLException ex) { + throw new EamDbException("Error inserting new global set.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement1); + closePreparedStatement(preparedStatement2); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Get a global set by ID + * + * @param globalSetID The ID to look up + * + * @return The global set associated with the ID + * + * @throws EamDbException + */ + @Override + public EamGlobalSet getGlobalSetByID(int globalSetID) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement1 = null; + ResultSet resultSet = null; + String sql1 = "SELECT * FROM global_reference_sets WHERE id=?"; + + try { + preparedStatement1 = conn.prepareStatement(sql1); + preparedStatement1.setInt(1, globalSetID); + resultSet = preparedStatement1.executeQuery(); + resultSet.next(); + return getEnterpriseArtifactManagerGlobalSetFromResultSet(resultSet); + + } catch (SQLException ex) { + throw new EamDbException("Error getting global set by id.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement1); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Add a new global file instance + * + * @param eamGlobalFileInstance The global file instance to add + * + * @throws EamDbException + */ + @Override + public void addGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + + String sql = "INSERT INTO global_files(global_reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?)"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setInt(1, eamGlobalFileInstance.getGlobalSetID()); + preparedStatement.setString(2, eamGlobalFileInstance.getMD5Hash()); + preparedStatement.setString(3, eamGlobalFileInstance.getKnownStatus().name()); + preparedStatement.setString(4, eamGlobalFileInstance.getComment()); + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error inserting new global file instance into global_files table.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Add a new global file instance to the bulk collection + * + * @param eamGlobalFileInstance The global file instance to add + * @throws EamDbException + */ + @Override + public void prepareGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException { + synchronized (bulkGlobalArtifacts) { + bulkGlobalArtifacts.get("FILES").add(eamGlobalFileInstance); // NON-NLS + bulkGlobalArtifactsCount++; + + if (bulkGlobalArtifactsCount >= bulkArtifactsThreshold) { + bulkInsertGlobalFileInstances(); + } + } + } + + /** + * Insert the bulk collection of Global File Instances + * + * @throws EamDbException + */ + @Override + public void bulkInsertGlobalFileInstances() throws EamDbException { + List artifactTypes = getCorrelationArtifactTypes(); + + Connection conn = connect(); + synchronized (bulkGlobalArtifacts) { + if (bulkGlobalArtifactsCount == 0) { + return; + } + + PreparedStatement bulkPs = null; + try { + for (EamArtifact.Type type : artifactTypes) { + String sql = "INSERT INTO global_files(global_reference_set_id, value, known_status, comment) VALUES (?, ?, ?, ?)"; + + bulkPs = conn.prepareStatement(sql); + + Collection eamGlobalFileInstances = bulkGlobalArtifacts.get(type.getName()); + for (EamGlobalFileInstance eamGlobalFileInstance : eamGlobalFileInstances) { + + bulkPs.setInt(1, eamGlobalFileInstance.getGlobalSetID()); + bulkPs.setString(2, eamGlobalFileInstance.getMD5Hash()); + bulkPs.setString(3, eamGlobalFileInstance.getKnownStatus().name()); + bulkPs.setString(4, eamGlobalFileInstance.getComment()); + bulkPs.addBatch(); + } + + bulkPs.executeBatch(); + bulkGlobalArtifacts.get(type.getName()).clear(); + } + + // Reset state + bulkGlobalArtifactsCount = 0; + } catch (SQLException ex) { + throw new EamDbException("Error inserting bulk artifacts.", ex); // NON-NLS + } finally { + closePreparedStatement(bulkPs); + closeConnection(conn); + } + } + } + + /** + * Get all global file instances having a given MD5 hash + * + * @param MD5Hash The hash to lookup + * @return List of all global file instances with a given hash + * @throws EamDbException + */ + @Override + public List getGlobalFileInstancesByHash(String MD5Hash) throws EamDbException { + Connection conn = connect(); + + List globalFileInstances = new ArrayList<>(); + PreparedStatement preparedStatement1 = null; + ResultSet resultSet = null; + String sql1 = "SELECT * FROM global_files WHERE value=?"; + + try { + preparedStatement1 = conn.prepareStatement(sql1); + preparedStatement1.setString(1, MD5Hash); + resultSet = preparedStatement1.executeQuery(); + while (resultSet.next()) { + globalFileInstances.add(getEnterpriseArtifactManagerGlobalFileInstanceFromResultSet(resultSet)); + } + return globalFileInstances; + + } catch (SQLException ex) { + throw new EamDbException("Error getting global set by id.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement1); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Add a new EamArtifact.Type to the db. + * + * @param newType New type to add. + * @throws EamDbException + */ + @Override + public void newCorrelationArtifactType(EamArtifact.Type newType) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + + String sql = "INSERT INTO artifact_types(name, supported, enabled) VALUES (?, ?, ?)"; + + try { + preparedStatement = conn.prepareStatement(sql); + + preparedStatement.setString(1, newType.getName()); + preparedStatement.setInt(2, newType.isSupported() ? 1 : 0); + preparedStatement.setInt(3, newType.isEnabled() ? 1 : 0); + + preparedStatement.executeUpdate(); + } catch (SQLException ex) { + throw new EamDbException("Error inserting new correlation artifact type.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + } + + /** + * Get the list of EamArtifact.Type's that will be used to correlate + * artifacts. + * + * @return List of EamArtifact.Type's. If none are defined in the database, + * the default list will be returned. + * @throws EamDbException + */ + @Override + public List getCorrelationArtifactTypes() throws EamDbException { + Connection conn = connect(); + + List aTypes = new ArrayList<>(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String sql = "SELECT * FROM artifact_types"; + + try { + preparedStatement = conn.prepareStatement(sql); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + aTypes.add(getCorrelationArtifactTypeFromResultSet(resultSet)); + } + return aTypes; + + } catch (SQLException ex) { + throw new EamDbException("Error getting all correlation artifact types.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Get the list of enabled EamArtifact.Type's that will be used to correlate + * artifacts. + * + * @return List of enabled EamArtifact.Type's. If none are defined in the + * database, the default list will be returned. + * @throws EamDbException + */ + @Override + public List getEnabledCorrelationArtifactTypes() throws EamDbException { + Connection conn = connect(); + + List aTypes = new ArrayList<>(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String sql = "SELECT * FROM artifact_types WHERE enabled=1"; + + try { + preparedStatement = conn.prepareStatement(sql); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + aTypes.add(getCorrelationArtifactTypeFromResultSet(resultSet)); + } + return aTypes; + + } catch (SQLException ex) { + throw new EamDbException("Error getting enabled correlation artifact types.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Get the list of supported EamArtifact.Type's that can be used to + * correlate artifacts. + * + * @return List of supported EamArtifact.Type's. If none are defined in the + * database, the default list will be returned. + * @throws EamDbException + */ + @Override + public List getSupportedCorrelationArtifactTypes() throws EamDbException { + Connection conn = connect(); + + List aTypes = new ArrayList<>(); + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String sql = "SELECT * FROM artifact_types WHERE supported=1"; + + try { + preparedStatement = conn.prepareStatement(sql); + resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + aTypes.add(getCorrelationArtifactTypeFromResultSet(resultSet)); + } + return aTypes; + + } catch (SQLException ex) { + throw new EamDbException("Error getting supported correlation artifact types.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Update a EamArtifact.Type. + * + * @param aType EamArtifact.Type to update. + * @throws EamDbException + */ + @Override + public void updateCorrelationArtifactType(EamArtifact.Type aType) throws EamDbException { + Connection conn = connect(); + + PreparedStatement preparedStatement = null; + String sql = "UPDATE artifact_types SET name=?, supported=?, enabled=? WHERE id=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, aType.getName()); + preparedStatement.setInt(2, aType.isSupported() ? 1 : 0); + preparedStatement.setInt(3, aType.isEnabled() ? 1 : 0); + preparedStatement.setInt(4, aType.getId()); + preparedStatement.executeUpdate(); + + } catch (SQLException ex) { + throw new EamDbException("Error getting correlation artifact type by name.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeConnection(conn); + } + + } + + /** + * Get the EamArtifact.Type that has name of typeName. + * + * @param typeName Name of Type to get + * + * @return EamArtifact.Type or null if it doesn't exist. + * @throws EamDbException + */ + @Override + public EamArtifact.Type getCorrelationArtifactTypeByName(String typeName) throws EamDbException { + Connection conn = connect(); + + EamArtifact.Type aType; + PreparedStatement preparedStatement = null; + ResultSet resultSet = null; + String sql = "SELECT * FROM artifact_types WHERE name=?"; + + try { + preparedStatement = conn.prepareStatement(sql); + preparedStatement.setString(1, typeName); + resultSet = preparedStatement.executeQuery(); + resultSet.next(); + aType = getCorrelationArtifactTypeFromResultSet(resultSet); + return aType; + + } catch (SQLException ex) { + throw new EamDbException("Error getting correlation artifact type by name.", ex); // NON-NLS + } finally { + closePreparedStatement(preparedStatement); + closeResultSet(resultSet); + closeConnection(conn); + } + } + + /** + * Convert a ResultSet to a EamCase object + * + * @param resultSet A resultSet with a set of values to create a EamCase + * object. + * + * @return fully populated EamCase object, or null + * + * @throws SQLException when an expected column name is not in the resultSet + */ + private EamCase getEnterpriseArtifactManagerCaseFromResultSet(ResultSet resultSet) throws SQLException { + if (null == resultSet) { + return null; + } + + EamOrganization eamOrg = null; + + resultSet.getInt("org_id"); + if (!resultSet.wasNull()) { + + eamOrg = new EamOrganization(resultSet.getInt("org_id"), + resultSet.getString("org_name"), + resultSet.getString("poc_name"), + resultSet.getString("poc_email"), + resultSet.getString("poc_phone")); + } + + EamCase eamCase = new EamCase(resultSet.getString("case_uid"), resultSet.getString("case_name")); + eamCase.setID(resultSet.getInt("case_id")); + eamCase.setOrg(eamOrg); + eamCase.setCreationDate(resultSet.getString("creation_date")); + eamCase.setCaseNumber(resultSet.getString("case_number")); + eamCase.setExaminerName(resultSet.getString("examiner_name")); + eamCase.setExaminerEmail(resultSet.getString("examiner_email")); + eamCase.setExaminerPhone(resultSet.getString("examiner_phone")); + eamCase.setNotes(resultSet.getString("notes")); + + return eamCase; + } + + private EamDataSource getEnterpriseArtifactManagerDataSourceFromResultSet(ResultSet resultSet) throws SQLException { + if (null == resultSet) { + return null; + } + + EamDataSource eamDataSource = new EamDataSource( + resultSet.getInt("id"), + resultSet.getString("device_id"), + resultSet.getString("name") + ); + + return eamDataSource; + } + + private EamArtifact.Type getCorrelationArtifactTypeFromResultSet(ResultSet resultSet) throws SQLException { + if (null == resultSet) { + return null; + } + + EamArtifact.Type eamArtifactType = new EamArtifact.Type( + resultSet.getInt("id"), + resultSet.getString("name"), + resultSet.getBoolean("supported"), + resultSet.getBoolean("enabled") + ); + + return eamArtifactType; + + } + + /** + * Convert a ResultSet to a EamArtifactInstance object + * + * @param resultSet A resultSet with a set of values to create a + * EamArtifactInstance object. + * + * @return fully populated EamArtifactInstance, or null + * + * @throws SQLException when an expected column name is not in the resultSet + */ + private EamArtifactInstance getEnterpriseArtifactManagerArtifactInstanceFromResultSet(ResultSet resultSet) throws SQLException { + if (null == resultSet) { + return null; + } + EamArtifactInstance eamArtifactInstance = new EamArtifactInstance( + new EamCase(resultSet.getString("case_uid"), resultSet.getString("case_name")), + new EamDataSource(resultSet.getString("device_id"), resultSet.getString("name")), + resultSet.getString("file_path"), + resultSet.getString("comment"), + EamArtifactInstance.KnownStatus.valueOf(resultSet.getString("known_status")), + EamArtifactInstance.GlobalStatus.LOCAL + ); + + return eamArtifactInstance; + } + + private EamOrganization getEnterpriseArtifactManagerOrganizationFromResultSet(ResultSet resultSet) throws SQLException { + if (null == resultSet) { + return null; + } + + EamOrganization eamOrganization = new EamOrganization( + resultSet.getInt("id"), + resultSet.getString("org_name"), + resultSet.getString("poc_name"), + resultSet.getString("poc_email"), + resultSet.getString("poc_phone") + ); + + return eamOrganization; + } + + private EamGlobalSet getEnterpriseArtifactManagerGlobalSetFromResultSet(ResultSet resultSet) throws SQLException { + if (null == resultSet) { + return null; + } + + EamGlobalSet eamGlobalSet = new EamGlobalSet( + resultSet.getInt("id"), + resultSet.getInt("org_id"), + resultSet.getString("set_name"), + resultSet.getString("version"), + LocalDate.parse(resultSet.getString("import_date")) + ); + + return eamGlobalSet; + } + + private EamGlobalFileInstance getEnterpriseArtifactManagerGlobalFileInstanceFromResultSet(ResultSet resultSet) throws SQLException { + if (null == resultSet) { + return null; + } + + EamGlobalFileInstance eamGlobalFileInstance = new EamGlobalFileInstance( + resultSet.getInt("id"), + resultSet.getInt("global_reference_set_id"), + resultSet.getString("value"), + EamArtifactInstance.KnownStatus.valueOf(resultSet.getString("known_status")), + resultSet.getString("comment") + ); + + return eamGlobalFileInstance; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifact.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifact.java new file mode 100644 index 0000000000..2a2da560ab --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifact.java @@ -0,0 +1,270 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * + * Used to store info about a specific artifact. + */ +public class EamArtifact implements Serializable { + + private static final long serialVersionUID = 1L; + + private String ID; + private String artifactValue; + private Type artifactType; + private final List artifactInstances; + + public EamArtifact(Type artifactType, String artifactValue) { + this.ID = ""; + this.artifactType = artifactType; + this.artifactValue = artifactValue; + this.artifactInstances = new ArrayList<>(); + } + + public Boolean equals(EamArtifact otherArtifact) { + return ((this.getID().equals(otherArtifact.getID())) + && (this.getArtifactType().equals(otherArtifact.getArtifactType())) + && (this.getArtifactValue().equals(otherArtifact.getArtifactValue())) + && (this.getInstances().equals(otherArtifact.getInstances()))); + } + + @Override + public String toString() { + String result = this.getID() + + this.getArtifactType().toString() + + this.getArtifactValue(); + result = this.getInstances().stream().map((inst) -> inst.toString()).reduce(result, String::concat); + return result; + } + + /** + * @return the ID + */ + public String getID() { + return ID; + } + + /** + * @param ID the ID to set + */ + public void setID(String ID) { + this.ID = ID; + } + + /** + * @return the artifactValue + */ + public String getArtifactValue() { + return artifactValue; + } + + /** + * @param artifactValue the artifactValue to set + */ + public void setArtifactValue(String artifactValue) { + this.artifactValue = artifactValue; + } + + /** + * @return the artifact Type + */ + public Type getArtifactType() { + return artifactType; + } + + /** + * @param artifactType the artifact Type to set + */ + public void setArtifactType(Type artifactType) { + this.artifactType = artifactType; + } + + /** + * @return the List of artifactInstances; empty list of none have been + * added. + */ + public List getInstances() { + return new ArrayList<>(artifactInstances); + } + + /** + * @param artifactInstances the List of artifactInstances to set. + */ + public void setInstances(List artifactInstances) { + this.artifactInstances.clear(); + if (null != artifactInstances) { + this.artifactInstances.addAll(artifactInstances); + } + } + + /** + * @param instance the instance to add + */ + public void addInstance(EamArtifactInstance artifactInstance) { + this.artifactInstances.add(artifactInstance); + } + + public static class Type implements Serializable { + + private int id; + private String name; + private Boolean supported; + private Boolean enabled; + + public Type(int id, String name, Boolean supported, Boolean enabled) { + this.id = id; + this.name = name; + this.supported = supported; + this.enabled = enabled; + } + + public Type(String name, Boolean supported, Boolean enabled) { + this(-1, name, supported, enabled); + } + + /** + * Determine if 2 Type objects are equal based on having the same + * Type.name. + * + * @param otherType Type object for comparison. + * + * @return true or false + */ + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } else if (!(that instanceof EamArtifact.Type)) { + return false; + } else { + return ((EamArtifact.Type) that).sameType(this); + } + } + + /** + * Determines if the content of this artifact type object is equivalent + * to the content of another artifact type object. + * + * @param that the other type + * + * @return true if it is the same type + */ + private boolean sameType(EamArtifact.Type that) { + return this.id == that.getId() + && this.name.equals(that.getName()) + && Objects.equals(this.supported, that.isSupported()) + && Objects.equals(this.enabled, that.isEnabled()); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 67 * hash + Objects.hashCode(this.id); + hash = 67 * hash + Objects.hashCode(this.name); + hash = 67 * hash + Objects.hashCode(this.supported); + hash = 67 * hash + Objects.hashCode(this.enabled); + return hash; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("(id=").append(id); + str.append(", name=").append(name); + str.append(", supported=").append(supported.toString()); + str.append(", enabled=").append(enabled.toString()); + str.append(")"); + return str.toString(); + } + + /** + * @return the id + */ + public int getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(int id) { + this.id = id; + } + + /** + * Get the name of this Artifact Type. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Set the name of this Artifact Type + * + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Check if this Artifact Type is supported. + * + * @return true or false + */ + public Boolean isSupported() { + return supported; + } + + /** + * Set this Artifact Type as supported or not supported. + * + * @param supported the supported to set + */ + public void setSupported(Boolean supported) { + this.supported = supported; + } + + /** + * Check if this Artifact Type is enabled. + * + * @return true or false + */ + public Boolean isEnabled() { + return enabled; + } + + /** + * Set this Artifact Type as enabled or not enabled. + * + * @param enabled the enabled to set + */ + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifactInstance.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifactInstance.java new file mode 100644 index 0000000000..9b0f940782 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifactInstance.java @@ -0,0 +1,251 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.io.Serializable; +import org.openide.util.NbBundle.Messages; + +/** + * + * Used to store info about a specific Artifact Instance. + * + */ +@Messages({"enterpriseartifactmanagerartifactinstances.globalStatus.local=Local", + "enterpriseartifactmanagerartifactinstances.globalStatus.global=Global", + "enterpriseartifactmanagerartifactinstances.knownStatus.bad=Bad", + "enterpriseartifactmanagerartifactinstances.knownStatus.known=Known", + "enterpriseartifactmanagerartifactinstances.knownStatus.unknown=Unknown"}) +public class EamArtifactInstance implements Serializable { + + public enum GlobalStatus { + LOCAL(Bundle.enterpriseartifactmanagerartifactinstances_globalStatus_local()), + GLOBAL(Bundle.enterpriseartifactmanagerartifactinstances_globalStatus_global()); + + private final String globalStatus; + + private GlobalStatus(String globalStatus) { + this.globalStatus = globalStatus; + } + + @Override + public String toString() { + return globalStatus; + } + } + + public enum KnownStatus { + UNKNOWN(Bundle.enterpriseartifactmanagerartifactinstances_knownStatus_unknown()), + KNOWN(Bundle.enterpriseartifactmanagerartifactinstances_knownStatus_known()), + BAD(Bundle.enterpriseartifactmanagerartifactinstances_knownStatus_bad()); + + private final String knownStatus; + + private KnownStatus(String knownStatus) { + this.knownStatus = knownStatus; + } + + @Override + public String toString() { + return knownStatus; + } + } + + private static final long serialVersionUID = 1L; + + private String ID; + private EamCase eamCase; + private EamDataSource eamDataSource; + private String filePath; + private String comment; + private KnownStatus knownStatus; + private GlobalStatus globalStatus; + + public EamArtifactInstance( + EamCase eamCase, + EamDataSource eamDataSource + ) { + this("", eamCase, eamDataSource, "", "", KnownStatus.UNKNOWN, GlobalStatus.LOCAL); + } + + public EamArtifactInstance( + EamCase eamCase, + EamDataSource eamDataSource, + String filePath + ) { + this("", eamCase, eamDataSource, filePath, "", KnownStatus.UNKNOWN, GlobalStatus.LOCAL); + } + + public EamArtifactInstance( + EamCase eamCase, + EamDataSource eamDataSource, + String filePath, + String comment + ) { + this("", eamCase, eamDataSource, filePath, comment, KnownStatus.UNKNOWN, GlobalStatus.LOCAL); + } + + public EamArtifactInstance( + EamCase eamCase, + EamDataSource eamDataSource, + String filePath, + String comment, + KnownStatus knownStatus, + GlobalStatus globalStatus + ) { + this("", eamCase, eamDataSource, filePath, comment, knownStatus, globalStatus); + } + + public EamArtifactInstance( + String ID, + EamCase eamCase, + EamDataSource eamDataSource, + String filePath, + String comment, + KnownStatus knownStatus, + GlobalStatus globalStatus + ) { + this.ID = ID; + this.eamCase = eamCase; + this.eamDataSource = eamDataSource; + this.filePath = filePath; + this.comment = comment; + this.knownStatus = knownStatus; + this.globalStatus = globalStatus; + } + + public Boolean equals(EamArtifactInstance otherInstance) { + return ((this.getID().equals(otherInstance.getID())) + && (this.getEamCase().equals(otherInstance.getEamCase())) + && (this.getEamDataSource().equals(otherInstance.getEamDataSource())) + && (this.getFilePath().equals(otherInstance.getFilePath())) + && (this.getGlobalStatus().equals(otherInstance.getGlobalStatus())) + && (this.getKnownStatus().equals(otherInstance.getKnownStatus())) + && (this.getComment().equals(otherInstance.getComment()))); + } + + @Override + public String toString() { + return this.getID() + + this.getEamCase().getCaseUUID() + + this.getEamDataSource().getName() + + this.getFilePath() + + this.getGlobalStatus() + + this.getKnownStatus() + + this.getComment(); + } + + /** + * @return the ID + */ + public String getID() { + return ID; + } + + /** + * @param ID the ID to set + */ + public void setID(String ID) { + this.ID = ID; + } + + /** + * @return the eamCase + */ + public EamCase getEamCase() { + return eamCase; + } + + /** + * @param eamCase the eamCase to set + */ + public void setEamCase(EamCase eamCase) { + this.eamCase = eamCase; + } + + /** + * @return the eamDataSource + */ + public EamDataSource getEamDataSource() { + return eamDataSource; + } + + /** + * @param eamDataSource the eamDataSource to set + */ + public void setEamDataSource(EamDataSource eamDataSource) { + this.eamDataSource = eamDataSource; + } + + /** + * @return the filePath + */ + public String getFilePath() { + return filePath; + } + + /** + * @param filePath the filePath to set + */ + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + /** + * @return the comment + */ + public String getComment() { + return comment; + } + + /** + * @param comment the comment to set + */ + public void setComment(String comment) { + this.comment = comment; + } + + /** + * @return the knownStatus + */ + public KnownStatus getKnownStatus() { + return knownStatus; + } + + /** + * @param knownStatus the knownStatus to set + */ + public void setKnownStatus(KnownStatus knownStatus) { + this.knownStatus = knownStatus; + } + + /** + * @return the globalStatus + */ + public GlobalStatus getGlobalStatus() { + return globalStatus; + } + + /** + * @param globalStatus the globalStatus to set + */ + public void setGlobalStatus(GlobalStatus globalStatus) { + this.globalStatus = globalStatus; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifactUtil.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifactUtil.java new file mode 100644 index 0000000000..a5cc8a76b5 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamArtifactUtil.java @@ -0,0 +1,163 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.util.List; +import java.util.logging.Level; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; + +/** + * + */ +public class EamArtifactUtil { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(EamArtifactUtil.class.getName()); + + public EamArtifactUtil() { + } + + @Messages({"enterpriseartifactmanagerartifact.emailaddresses.text=Email Addresses"}) + public static String getEmailAddressAttrString() { + return Bundle.enterpriseartifactmanagerartifact_emailaddresses_text(); + } + + /* + * Static factory method to examine a BlackboardArtifact to determine if it + * has contents that can be used for Correlation. If so, return a + * EamArtifact with a single EamArtifactInstance within. If not, return + * null. + * + * @param bbArtifact BlackboardArtifact to examine @return EamArtifact or + * null + */ + public static EamArtifact fromBlackboardArtifact(BlackboardArtifact bbArtifact, + boolean includeInstances, + List artifactTypes, + boolean checkEnabled) { + + EamArtifact eamArtifact = null; + for (EamArtifact.Type aType : artifactTypes) { + if ((checkEnabled && aType.isEnabled()) || !checkEnabled) { + eamArtifact = getTypeFromBlackboardArtifact(aType, bbArtifact); + } + if (null != eamArtifact) { + break; + } + } + + if (null != eamArtifact && includeInstances) { + try { + AbstractFile af = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); + if (null == af) { + return null; + } + + String deviceId = ""; + try { + deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(af.getDataSource().getId()).getDeviceId(); + } catch (TskCoreException | TskDataException ex) { + LOGGER.log(Level.SEVERE, "Error, failed to get deviceID or data source from current case.", ex); + } + + EamArtifactInstance eamInstance = new EamArtifactInstance( + new EamCase(Case.getCurrentCase().getName(), Case.getCurrentCase().getDisplayName()), + new EamDataSource(deviceId, af.getDataSource().getName()), + af.getParentPath() + af.getName(), + "", + EamArtifactInstance.KnownStatus.UNKNOWN, + EamArtifactInstance.GlobalStatus.LOCAL + ); + eamArtifact.addInstance(eamInstance); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS + return null; + } + } + + return eamArtifact; + } + + /** + * Convert a blackboard artifact to an EamArtifact + * + * @param aType The enterprise artifact manager artifact type to create + * @param bbArtifact The blackboard artifact to convert + * + * @return + */ + public static EamArtifact getTypeFromBlackboardArtifact(EamArtifact.Type aType, BlackboardArtifact bbArtifact) { + String value = null; + int artifactTypeID = bbArtifact.getArtifactTypeID(); + + try { + if (aType.getName().equals("EMAIL") + && BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() == artifactTypeID) { + + BlackboardAttribute setNameAttr = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME)); + if (setNameAttr != null + && EamArtifactUtil.getEmailAddressAttrString().equals(setNameAttr.getValueString())) { + value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD)).getValueString(); + } + } else if (aType.getName().equals("DOMAIN") + && (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() == artifactTypeID + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID() == artifactTypeID + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifactTypeID + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID() == artifactTypeID)) { + + value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN)).getValueString(); + + } else if (aType.getName().equals("PHONE") + && (BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID() == artifactTypeID + || BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() == artifactTypeID + || BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == artifactTypeID)) { + + if (null != bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER))) { + value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)).getValueString(); + } else if (null != bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM))) { + value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)).getValueString(); + } else if (null != bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO))) { + value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)).getValueString(); + } + + } else if (aType.getName().equals("USBID") + && BlackboardArtifact.ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID() == artifactTypeID) { + + value = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID)).getValueString(); + } + + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS + return null; + } + + if (null != value) { + return new EamArtifact(aType, value); + } else { + return null; + } + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamCase.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamCase.java new file mode 100644 index 0000000000..bb00175936 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamCase.java @@ -0,0 +1,291 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.io.Serializable; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import org.openide.util.NbBundle.Messages; + +/** + * + * Used to store info about a case. + * + */ +public class EamCase implements Serializable { + + private static long serialVersionUID = 1L; + private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss (z)"); + + private int ID; + private String caseUUID; + private EamOrganization org; + private String displayName; + private String creationDate; + private String caseNumber; + private String examinerName; + private String examinerEmail; + private String examinerPhone; + private String notes; + + public EamCase(String caseUUID, String displayName) { + this(-1, caseUUID, null, displayName, DATE_FORMAT.format(new Date()), "", "", "", "", ""); + } + + public EamCase(int ID, + String caseUUID, + EamOrganization org, + String displayName, + String creationDate, + String caseNumber, + String examinerName, + String examinerEmail, + String examinerPhone, + String notes) { + this.ID = ID; + this.caseUUID = caseUUID; + this.org = org; + this.displayName = displayName; + this.creationDate = creationDate; + this.caseNumber = caseNumber; + this.examinerName = examinerName; + this.examinerEmail = examinerEmail; + this.examinerPhone = examinerPhone; + this.notes = notes; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("("); + str.append("ID=").append(Integer.toString(getID())); + str.append(",UUID=").append(getCaseUUID()); + str.append(",organization=").append(getOrg().toString()); + str.append(",displayName=").append(getDisplayName()); + str.append(",creationDate=").append(getCreationDate()); + str.append(",caseNumber=").append(getCaseNumber()); + str.append(",examinerName=").append(getExaminerName()); + str.append(",examinerEmail=").append(getExaminerEmail()); + str.append(",examinerPhone=").append(getExaminerPhone()); + str.append(",notes=").append(getNotes()); + str.append(")"); + return str.toString(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.caseUUID=Case UUID: "}) + public String getTitleCaseUUID() { + return Bundle.EnterpriseArtifactManagerCase_title_caseUUID(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.creationDate=Creation Date: "}) + public String getTitleCreationDate() { + return Bundle.EnterpriseArtifactManagerCase_title_creationDate(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.caseDisplayName=Case Name: "}) + public String getTitleCaseDisplayName() { + return Bundle.EnterpriseArtifactManagerCase_title_caseDisplayName(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.caseNumber=Case Number: "}) + public String getTitleCaseNumber() { + return Bundle.EnterpriseArtifactManagerCase_title_caseNumber(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.examinerName=Examiner Name: "}) + public String getTitleExaminerName() { + return Bundle.EnterpriseArtifactManagerCase_title_examinerName(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.examinerEmail=Examiner Email: "}) + public String getTitleExaminerEmail() { + return Bundle.EnterpriseArtifactManagerCase_title_examinerEmail(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.examinerPhone=Examiner Phone: "}) + public String getTitleExaminerPhone() { + return Bundle.EnterpriseArtifactManagerCase_title_examinerPhone(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.org=Organization: "}) + public String getTitleOrganization() { + return Bundle.EnterpriseArtifactManagerCase_title_org(); + } + + @Messages({"EnterpriseArtifactManagerCase.title.notes=Notes: "}) + public String getTitleNotes() { + return Bundle.EnterpriseArtifactManagerCase_title_notes(); + } + + public String getCaseDetailsOptionsPaneDialog() { + StringBuilder content = new StringBuilder(); + content.append(getTitleCaseUUID()).append(getCaseUUID().toString()).append("\n"); + content.append(getTitleCaseDisplayName()).append(getDisplayName()).append("\n"); + content.append(getTitleCreationDate()).append(getCreationDate()).append("\n"); + content.append(getTitleCaseNumber()).append(getCaseNumber()).append("\n"); + content.append(getTitleExaminerName()).append(getExaminerName()).append("\n"); + content.append(getTitleExaminerEmail()).append(getExaminerEmail()).append("\n"); + content.append(getTitleExaminerPhone()).append(getExaminerPhone()).append("\n"); + content.append(getTitleNotes()).append(getNotes()).append("\n"); + + return content.toString(); + } + + /** + * @return the ID + */ + public int getID() { + return ID; + } + + /** + * @param ID the ID to set + */ + public void setID(int ID) { + this.ID = ID; + } + + /** + * @return the caseUUID + */ + public String getCaseUUID() { + return caseUUID; + } + + /** + * @param caseUUID the caseUUID to set + */ + public void setCaseUUID(String caseUUID) { + this.caseUUID = caseUUID; + } + + /** + * @return the org + */ + public EamOrganization getOrg() { + return org; + } + + /** + * @param org the org to set + */ + public void setOrg(EamOrganization org) { + this.org = org; + } + + /** + * @return the displayName + */ + public String getDisplayName() { + return displayName; + } + + /** + * @param displayName the displayName to set + */ + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + /** + * @return the creationDate + */ + public String getCreationDate() { + return creationDate; + } + + /** + * @param creationDate the creationDate to set + */ + public void setCreationDate(String creationDate) { + this.creationDate = creationDate; + } + + /** + * @return the caseNumber + */ + public String getCaseNumber() { + return caseNumber; + } + + /** + * @param caseNumber the caseNumber to set + */ + public void setCaseNumber(String caseNumber) { + this.caseNumber = caseNumber; + } + + /** + * @return the examinerName + */ + public String getExaminerName() { + return examinerName; + } + + /** + * @param examinerName the examinerName to set + */ + public void setExaminerName(String examinerName) { + this.examinerName = examinerName; + } + + /** + * @return the examinerEmail + */ + public String getExaminerEmail() { + return examinerEmail; + } + + /** + * @param examinerEmail the examinerEmail to set + */ + public void setExaminerEmail(String examinerEmail) { + this.examinerEmail = examinerEmail; + } + + /** + * @return the examinerPhone + */ + public String getExaminerPhone() { + return examinerPhone; + } + + /** + * @param examinerPhone the examinerPhone to set + */ + public void setExaminerPhone(String examinerPhone) { + this.examinerPhone = examinerPhone; + } + + /** + * @return the notes + */ + public String getNotes() { + return notes; + } + + /** + * @param notes the notes to set + */ + public void setNotes(String notes) { + this.notes = notes; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDataSource.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDataSource.java new file mode 100644 index 0000000000..448a7586c2 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDataSource.java @@ -0,0 +1,101 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.io.Serializable; + +/** + * + * Used to store info about a case. + * + */ +public class EamDataSource implements Serializable { + + private static long serialVersionUID = 1L; + + private int ID; + private String deviceID; + private String name; + + public EamDataSource(String deviceID, String name) { + this(-1, deviceID, name); + } + + public EamDataSource(int ID, + String deviceID, + String name) { + this.ID = ID; + this.deviceID = deviceID; + this.name = name; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("("); + str.append("ID=").append(Integer.toString(getID())); + str.append(",deviceID=").append(getDeviceID()); + str.append(",name=").append(getName()); + str.append(")"); + return str.toString(); + } + + /** + * @return the ID + */ + public int getID() { + return ID; + } + + /** + * @param ID the ID to set + */ + public void setID(int ID) { + this.ID = ID; + } + + /** + * @return the deviceID + */ + public String getDeviceID() { + return deviceID; + } + + /** + * @param deviceID the deviceID to set + */ + public void setDeviceID(String deviceID) { + this.deviceID = deviceID; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDb.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDb.java new file mode 100644 index 0000000000..b191a5abb0 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDb.java @@ -0,0 +1,484 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.util.List; + +/** + * Main interface for interacting with the database + */ +public interface EamDb { + + /** + * Get the instance; default to SQLITE. + * + * @return The EamDb instance + * + * @throws EamDbException + */ + static EamDb getInstance() { + EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform(); + + switch (selectedPlatform) { + case POSTGRESQL: + return PostgresEamDb.getInstance(); + + case SQLITE: + default: + return SqliteEamDb.getInstance(); + } + } + + /** + * Update settings + */ + void updateSettings(); + + /** + * Save settings + */ + void saveSettings(); + + /** + * Reset the database (testing method) + */ + void reset() throws EamDbException; + + /** + * Is the database enabled? + * + * @return Is the database enabled + */ + boolean isEnabled(); + + /** + * Get the list of tags recognized as "Bad" + * + * @return The list of bad tags + */ + List getBadTags(); + + /** + * Set the tags recognized as "Bad" + * + * @param tags The tags to consider bad + */ + void setBadTags(List tags); + + /** + * Add a new name/value pair in the db_info table. + * + * @param name Key to set + * @param value Value to set + * + * @throws EamDbException + */ + public void newDbInfo(String name, String value) throws EamDbException; + + /** + * Get the value for the given name from the name/value db_info table. + * + * @param name Name to search for + * + * @return value associated with name. + * + * @throws EamDbException + */ + public String getDbInfo(String name) throws EamDbException; + + /** + * Update the value for a name in the name/value db_info table. + * + * @param name Name to find + * @param value Value to assign to name. + * + * @throws EamDbException + */ + public void updateDbInfo(String name, String value) throws EamDbException; + + /** + * Creates new Case in the database + * + * Expects the Organization for this case to already exist in the database. + * + * @param eamCase The case to add + */ + void newCase(EamCase eamCase) throws EamDbException; + + /** + * Updates an existing Case in the database + * + * @param eamCase The case to update + */ + void updateCase(EamCase eamCase) throws EamDbException; + + /** + * Retrieves Case details based on Case UUID + * + * @param caseUUID unique identifier for a case + * + * @return The retrieved case + */ + EamCase getCaseDetails(String caseUUID) throws EamDbException; + + /** + * Retrieves cases that are in DB. + * + * @return List of cases + */ + List getCases() throws EamDbException; + + /** + * Creates new Data Source in the database + * + * @param eamDataSource the data source to add + */ + void newDataSource(EamDataSource eamDataSource) throws EamDbException; + + /** + * Updates a Data Source in the database + * + * @param eamDataSource the data source to update + */ + void updateDataSource(EamDataSource eamDataSource) throws EamDbException; + + /** + * Retrieves Data Source details based on data source device ID + * + * @param dataSourceDeviceId the data source device ID number + * + * @return The data source + */ + EamDataSource getDataSourceDetails(String dataSourceDeviceId) throws EamDbException; + + /** + * Retrieves data sources that are in DB + * + * @return List of data sources + */ + List getDataSources() throws EamDbException; + + /** + * Inserts new Artifact(s) into the database. Should add associated Case and + * Data Source first. + * + * @param eamArtifact The artifact to add + */ + void addArtifact(EamArtifact eamArtifact) throws EamDbException; + + /** + * Retrieves eamArtifact instances from the database that are associated with + * the eamArtifactType and eamArtifactValue of the given eamArtifact. + * + * @param eamArtifact The type/value to look up (artifact with 0 instances) + * + * @return List of artifact instances for a given type/value + */ + List getArtifactInstancesByTypeValue(EamArtifact eamArtifact) throws EamDbException; + + /** + * Retrieves eamArtifact instances from the database that are associated with + * the aType and filePath + * + * @param aType EamArtifact.Type to search for + * @param filePath File path to search for + * + * @return List of 0 or more EnterpriseArtifactManagerArtifactInstances + * + * @throws EamDbException + */ + List getArtifactInstancesByPath(EamArtifact.Type aType, String filePath) throws EamDbException; + + /** + * Retrieves number of artifact instances in the database that are + * associated with the ArtifactType and artifactValue of the given artifact. + * + * @param eamArtifact Artifact with artifactType and artifactValue to search + * for + * + * @return Number of artifact instances having ArtifactType and + * ArtifactValue. + */ + Long getCountArtifactInstancesByTypeValue(EamArtifact eamArtifact) throws EamDbException; + + /** + * Using the ArtifactType and ArtifactValue from the given eamArtfact, + * compute the ratio of: (The number of unique case_id/datasource_id tuples + * where Type/Value is found) divided by (The total number of unique + * case_id/datasource_id tuples in the database) expressed as a percentage. + * + * @param eamArtifact Artifact with artifactType and artifactValue to search + * for + * + * @return Int between 0 and 100 + */ + int getCommonalityPercentageForTypeValue(EamArtifact eamArtifact) throws EamDbException; + + /** + * Retrieves number of unique caseDisplayName / dataSource tuples in the + * database that are associated with the artifactType and artifactValue of + * the given artifact. + * + * @param eamArtifact Artifact with artifactType and artifactValue to search + * for + * + * @return Number of unique tuples + */ + Long getCountUniqueCaseDataSourceTuplesHavingTypeValue(EamArtifact eamArtifact) throws EamDbException; + + /** + * Retrieves number of unique caseDisplayName/dataSource tuples in the + * database. + * + * @return Number of unique tuples + */ + Long getCountUniqueCaseDataSourceTuples() throws EamDbException; + + /** + * Retrieves number of eamArtifact instances in the database that are + * associated with the caseDisplayName and dataSource of the given + * eamArtifact instance. + * + * @param eamInstance Instance with caseName and dataSource to search for + * + * @param eamInstance Instance with caseDisplayName and dataSource to search + * for + * + * @return Number of artifact instances having caseDisplayName and + * dataSource + */ + Long getCountArtifactInstancesByCaseDataSource(EamArtifactInstance eamInstance) throws EamDbException; + + /** + * Adds an eamArtifact to an internal list to be later added to DB. Artifact + * can have 1 or more Artifact Instances. Insert will be triggered by a + * threshold or a call to bulkInsertArtifacts(). + * + * @param eamArtifact The artifact to add + */ + void prepareBulkArtifact(EamArtifact eamArtifact) throws EamDbException; + + /** + * Executes a bulk insert of the eamArtifacts added from the + * prepareBulkArtifact() method + */ + void bulkInsertArtifacts() throws EamDbException; + + /** + * Executes a bulk insert of the cases + */ + void bulkInsertCases(List cases) throws EamDbException; + + /** + * Sets an eamArtifact instance as knownStatus = "Bad". If eamArtifact exists, + * it is updated. If eamArtifact does not exist nothing happens + * + * @param eamArtifact Artifact containing exactly one (1) ArtifactInstance. + */ + void setArtifactInstanceKnownBad(EamArtifact eamArtifact) throws EamDbException; + + /** + * Gets list of matching eamArtifact instances that have knownStatus = "Bad". + * + * @param eamArtifact Artifact containing Type and Value + * + * @return List with 0 or more matching eamArtifact instances. + */ + List getArtifactInstancesKnownBad(EamArtifact eamArtifact) throws EamDbException; + + /** + * Count matching eamArtifacts instances that have knownStatus = "Bad". + * + * @param eamArtifact Artifact containing Type and Value + * + * @return Number of matching eamArtifacts + */ + Long getCountArtifactInstancesKnownBad(EamArtifact eamArtifact) throws EamDbException; + + /** + * Gets list of distinct case display names, where each case has 1+ Artifact + * Instance matching eamArtifact with knownStatus = "Bad". + * + * @param eamArtifact Artifact containing Type and Value + * + * @return List of cases containing this artifact with instances marked as + * bad + * + * @throws EamDbException + */ + List getListCasesHavingArtifactInstancesKnownBad(EamArtifact eamArtifact) throws EamDbException; + + /** + * Is the artifact globally known as bad? + * + * @param eamArtifact Artifact containing Type and Value + * + * @return Global known status of the artifact + */ + boolean isArtifactGlobalKnownBad(EamArtifact eamArtifact) throws EamDbException; + + /** + * Add a new organization + * + * @param eamOrg The organization to add + * + * @throws EamDbException + */ + void newOrganization(EamOrganization eamOrg) throws EamDbException; + + /** + * Get all organizations + * + * @return A list of all organizations + * + * @throws EamDbException + */ + List getOrganizations() throws EamDbException; + + /** + * Get an organization having the given ID + * + * @param orgID The id to look up + * + * @return The organization with the given ID + * + * @throws EamDbException + */ + EamOrganization getOrganizationByID(int orgID) throws EamDbException; + + /** + * Add a new Global Set + * + * @param eamGlobalSet The global set to add + * + * @return The ID of the new global set + * + * @throws EamDbException + */ + int newGlobalSet(EamGlobalSet eamGlobalSet) throws EamDbException; + + /** + * Get a global set by ID + * + * @param globalSetID The ID to look up + * + * @return The global set associated with the ID + * + * @throws EamDbException + */ + EamGlobalSet getGlobalSetByID(int globalSetID) throws EamDbException; + + /** + * Add a new global file instance + * + * @param eamGlobalFileInstance The global file instance to add + * + * @throws EamDbException + */ + void addGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException; + + /** + * Add a new global file instance to the bulk collection + * + * @param eamGlobalFileInstance The global file instance to add + * + * @throws EamDbException + */ + void prepareGlobalFileInstance(EamGlobalFileInstance eamGlobalFileInstance) throws EamDbException; + + /** + * Insert the bulk collection of Global File Instances + * + * @throws EamDbException + */ + void bulkInsertGlobalFileInstances() throws EamDbException; + + /** + * Get all global file instances having a given MD5 hash + * + * @param MD5Hash The hash to lookup + * + * @return List of all global file instances with a given hash + * + * @throws EamDbException + */ + List getGlobalFileInstancesByHash(String MD5Hash) throws EamDbException; + + /** + * Add a new EamArtifact.Type to the db. + * + * @param newType New type to add. + * + * @throws EamDbException + */ + public void newCorrelationArtifactType(EamArtifact.Type newType) throws EamDbException; + + /** + * Get the list of EamArtifact.Type's that will be used to correlate + * artifacts. + * + * @return List of EamArtifact.Type's. If none are defined in the database, + * the default list will be returned. + * + * @throws EamDbException + */ + public List getCorrelationArtifactTypes() throws EamDbException; + + /** + * Get the list of enabled EamArtifact.Type's that will be used to correlate + * artifacts. + * + * @return List of enabled EamArtifact.Type's. If none are defined in the + * database, the default list will be returned. + * + * @throws EamDbException + */ + public List getEnabledCorrelationArtifactTypes() throws EamDbException; + + /** + * Get the list of supported EamArtifact.Type's that can be used to + * correlate artifacts. + * + * @return List of supported EamArtifact.Type's. If none are defined in the + * database, the default list will be returned. + * + * @throws EamDbException + */ + public List getSupportedCorrelationArtifactTypes() throws EamDbException; + + /** + * Update a EamArtifact.Type. + * + * @param aType EamArtifact.Type to update. + * + * @throws EamDbException + */ + public void updateCorrelationArtifactType(EamArtifact.Type aType) throws EamDbException; + + /** + * Get the EamArtifact.Type that has name of typeName. + * + * @param typeName Name of Type to get + * + * @return EamArtifact.Type or null if it doesn't exist. + * + * @throws EamDbException + */ + public EamArtifact.Type getCorrelationArtifactTypeByName(String typeName) throws EamDbException; +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDbException.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDbException.java new file mode 100644 index 0000000000..a0c32dfe7d --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDbException.java @@ -0,0 +1,47 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +/* + * An exception to be thrown by an artifact manager. + */ +public class EamDbException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an exception to be thrown by an artifact manager. + * + * @param message The exception message. + */ + public EamDbException(String message) { + super(message); + } + + /** + * Constructs an exception to be thrown by an artifact manager. + * + * @param message The exception message. + * @param cause The exception cause. + */ + public EamDbException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDbPlatformEnum.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDbPlatformEnum.java new file mode 100644 index 0000000000..a0e8545243 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamDbPlatformEnum.java @@ -0,0 +1,116 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import org.sleuthkit.autopsy.coreutils.ModuleSettings; + +/** + * + */ +public enum EamDbPlatformEnum { + SQLITE("SQLite", true), + POSTGRESQL("PostgreSQL", false); + + private final String platformName; + private Boolean selected; + + EamDbPlatformEnum(String name, Boolean selected) { + this.platformName = name; + this.selected = selected; + loadSettings(); + } + + /** + * Load the selectedPlatform boolean from the config file, if it is set. + */ + private void loadSettings() { + String selectedPlatformString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.selectedPlatform"); // NON-NLS + + if (null != selectedPlatformString) { + selected = this.toString().equalsIgnoreCase(selectedPlatformString); + } + } + + @Override + public String toString() { + return platformName; + } + + private void setSelected(Boolean selected) { + this.selected = selected; + } + + private Boolean isSelected() { + return selected; + } + + public static EamDbPlatformEnum fromString(String pName) { + if (null == pName) { + return SQLITE; + } + + for (EamDbPlatformEnum p : EamDbPlatformEnum.values()) { + if (p.toString().equalsIgnoreCase(pName)) { + return p; + } + } + return SQLITE; + } + + /** + * Save the selected platform to the config file. + */ + public static void saveSelectedPlatform() { + EamDbPlatformEnum selectedPlatform = SQLITE; + for (EamDbPlatformEnum p : EamDbPlatformEnum.values()) { + if (p.isSelected()) { + selectedPlatform = p; + } + } + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.selectedPlatform", selectedPlatform.name()); // NON-NLS + } + + /** + * Set the selected db platform. Other platforms will be set as not + * selected. + * + * @param platformString The name of the selected platform. + */ + public static void setSelectedPlatform(String platformString) { + EamDbPlatformEnum pSelected = EamDbPlatformEnum.fromString(platformString); + for (EamDbPlatformEnum p : EamDbPlatformEnum.values()) { + p.setSelected(p == pSelected); + } + } + + /** + * Get the selected platform. + * + * @return The selected platform, or if not platform is selected, default to + * SQLITE. + */ + public static EamDbPlatformEnum getSelectedPlatform() { + for (EamDbPlatformEnum p : EamDbPlatformEnum.values()) { + if (p.isSelected()) { + return p; + } + } + return SQLITE; + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamGlobalFileInstance.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamGlobalFileInstance.java new file mode 100644 index 0000000000..e9594d5f82 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamGlobalFileInstance.java @@ -0,0 +1,124 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactInstance.KnownStatus; + +/** + * Global file hash instance + */ +public class EamGlobalFileInstance { + + private int instanceID; + private int globalSetID; + private String MD5Hash; + private KnownStatus knownStatus; + private String comment; + + public EamGlobalFileInstance( + int instanceID, + int globalSetID, + String MD5Hash, + KnownStatus knownStatus, + String comment) { + this.instanceID = instanceID; + this.globalSetID = globalSetID; + this.MD5Hash = MD5Hash; + this.knownStatus = knownStatus; + this.comment = comment; + } + + public EamGlobalFileInstance( + int globalSetID, + String MD5Hash, + KnownStatus knownStatus, + String comment) { + this(-1, globalSetID, MD5Hash, knownStatus, comment); + } + + /** + * @return the instanceID + */ + public int getInstanceID() { + return instanceID; + } + + /** + * @param instanceID the instanceID to set + */ + public void setInstanceID(int instanceID) { + this.instanceID = instanceID; + } + + /** + * @return the globalSetID + */ + public int getGlobalSetID() { + return globalSetID; + } + + /** + * @param globalSetID the globalSetID to set + */ + public void setGlobalSetID(int globalSetID) { + this.globalSetID = globalSetID; + } + + /** + * @return the MD5Hash + */ + public String getMD5Hash() { + return MD5Hash; + } + + /** + * @param MD5Hash the MD5Hash to set + */ + public void setMD5Hash(String MD5Hash) { + this.MD5Hash = MD5Hash; + } + + /** + * @return the knownStatus + */ + public KnownStatus getKnownStatus() { + return knownStatus; + } + + /** + * @param knownStatus the knownStatus to set + */ + public void setKnownStatus(KnownStatus knownStatus) { + this.knownStatus = knownStatus; + } + + /** + * @return the comment + */ + public String getComment() { + return comment; + } + + /** + * @param comment the comment to set + */ + public void setComment(String comment) { + this.comment = comment; + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamGlobalSet.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamGlobalSet.java new file mode 100644 index 0000000000..e708d87e2e --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamGlobalSet.java @@ -0,0 +1,124 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.time.LocalDate; + +/** + * A global set in the enterprise artifact manager database + */ +public class EamGlobalSet { + + private int globalSetID; + private int orgID; + private String setName; + private String version; + private LocalDate importDate; + + public EamGlobalSet( + int globalSetID, + int orgID, + String setName, + String version, + LocalDate importDate) { + this.globalSetID = globalSetID; + this.orgID = orgID; + this.setName = setName; + this.version = version; + this.importDate = importDate; + } + + public EamGlobalSet( + int orgID, + String setName, + String version, + LocalDate importDate) { + this(-1, orgID, setName, version, importDate); + } + + /** + * @return the globalSetID + */ + public int getGlobalSetID() { + return globalSetID; + } + + /** + * @param globalSetID the globalSetID to set + */ + public void setGlobalSetID(int globalSetID) { + this.globalSetID = globalSetID; + } + + /** + * @return the orgID + */ + public int getOrgID() { + return orgID; + } + + /** + * @param orgID the orgID to set + */ + public void setOrgID(int orgID) { + this.orgID = orgID; + } + + /** + * @return the setName + */ + public String getSetName() { + return setName; + } + + /** + * @param setName the setName to set + */ + public void setSetName(String setName) { + this.setName = setName; + } + + /** + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(String version) { + this.version = version; + } + + /** + * @return the importDate + */ + public LocalDate getImportDate() { + return importDate; + } + + /** + * @param importDate the importDate to set + */ + public void setImportDate(LocalDate importDate) { + this.importDate = importDate; + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamOrganization.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamOrganization.java new file mode 100644 index 0000000000..db0947720f --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/EamOrganization.java @@ -0,0 +1,146 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +/** + * An organization in the enterprise artifact manager database + */ +public class EamOrganization { + + private int orgID; + private String name; + private String pocName; + private String pocEmail; + private String pocPhone; + + public EamOrganization( + int orgID, + String name, + String pocName, + String pocEmail, + String pocPhone) { + this.orgID = orgID; + this.name = name; + this.pocName = pocName; + this.pocEmail = pocEmail; + this.pocPhone = pocPhone; + } + + public EamOrganization( + String name, + String pocName, + String pocEmail, + String pocPhone) { + this(-1, name, pocName, pocEmail, pocPhone); + } + + public EamOrganization( + String name) { + this(-1, name, "", "", ""); + } + + public static EamOrganization getDefault() { + // TODO: when we allow the user to configure/specify the default organization + // this will return it. + return null; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("("); + str.append("orgID=").append(Integer.toString(getOrgID())); + str.append("name=").append(getName()); + str.append("pocName=").append(getPocName()); + str.append("pocEmail=").append(getPocEmail()); + str.append("pocPhone=").append(getPocPhone()); + str.append(")"); + return str.toString(); + } + + /** + * @return the orgID + */ + public int getOrgID() { + return orgID; + } + + /** + * @param orgID the orgID to set + */ + public void setOrgID(int orgID) { + this.orgID = orgID; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the pocName + */ + public String getPocName() { + return pocName; + } + + /** + * @param pocName the pocName to set + */ + public void setPocName(String pocName) { + this.pocName = pocName; + } + + /** + * @return the pocEmail + */ + public String getPocEmail() { + return pocEmail; + } + + /** + * @param pocEmail the pocEmail to set + */ + public void setPocEmail(String pocEmail) { + this.pocEmail = pocEmail; + } + + /** + * @return the pocPhone + */ + public String getPocPhone() { + return pocPhone; + } + + /** + * @param pocPhone the pocPhone to set + */ + public void setPocPhone(String pocPhone) { + this.pocPhone = pocPhone; + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/PostgresEamDb.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/PostgresEamDb.java new file mode 100644 index 0000000000..cdc835bca2 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/PostgresEamDb.java @@ -0,0 +1,339 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import org.apache.commons.dbcp2.BasicDataSource; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Enterprise artifact manager database implementation using Postgres as a + * backend + */ +public class PostgresEamDb extends AbstractSqlEamDb { + + private final static Logger LOGGER = Logger.getLogger(PostgresEamDb.class.getName()); + + private static PostgresEamDb instance; + + private static final int CONN_POOL_SIZE = 10; + private BasicDataSource connectionPool = null; + + private final PostgresEamDbSettings dbSettings; + + public synchronized static PostgresEamDb getInstance() { + if (instance == null) { + instance = new PostgresEamDb(); + } + + return instance; + } + + private PostgresEamDb() { + dbSettings = new PostgresEamDbSettings(); + updateSettings(); + } + + @Override + public void updateSettings() { + synchronized (this) { + dbSettings.loadSettings(); + bulkArtifactsThreshold = dbSettings.getBulkThreshold(); + } + } + + @Override + public void saveSettings() { + synchronized (this) { + dbSettings.saveSettings(); + } + } + + @Override + public void reset() throws EamDbException { + Connection conn = connect(); + + try { + Statement dropContent = conn.createStatement(); + dropContent.executeUpdate("TRUNCATE TABLE organizations RESTART IDENTITY CASCADE"); + dropContent.executeUpdate("TRUNCATE TABLE cases RESTART IDENTITY CASCADE"); + dropContent.executeUpdate("TRUNCATE TABLE data_sources RESTART IDENTITY CASCADE"); + dropContent.executeUpdate("TRUNCATE TABLE global_reference_sets RESTART IDENTITY CASCADE"); + dropContent.executeUpdate("TRUNCATE TABLE global_files RESTART IDENTITY CASCADE"); + dropContent.executeUpdate("TRUNCATE TABLE artifact_types RESTART IDENTITY CASCADE"); + dropContent.executeUpdate("TRUNCATE TABLE db_info RESTART IDENTITY CASCADE"); + + String instancesTemplate = "TRUNCATE TABLE %s_instances RESTART IDENTITY CASCADE"; + for (EamArtifact.Type type : DEFAULT_ARTIFACT_TYPES) { + dropContent.executeUpdate(String.format(instancesTemplate, type.getName().toLowerCase())); + } + + } catch (SQLException ex) { + //LOGGER.log(Level.WARNING, "Failed to reset database.", ex); + } finally { + closeConnection(conn); + } + + insertDefaultContent(); + } + + /** + * Setup a connection pool for db connections. + * + */ + private void setupConnectionPool() throws EamDbException { + connectionPool = new BasicDataSource(); + connectionPool.setUsername(dbSettings.getUserName()); + connectionPool.setPassword(dbSettings.getPassword()); + connectionPool.setDriverClassName(dbSettings.getDriver()); + + StringBuilder connectionURL = new StringBuilder(); + connectionURL.append(dbSettings.getJDBCBaseURI()); + connectionURL.append(dbSettings.getHost()); + connectionURL.append(":"); + connectionURL.append(dbSettings.getPort()); + connectionURL.append("/"); + connectionURL.append(dbSettings.getDbName()); + connectionURL.append("?user="); + connectionURL.append(dbSettings.getUserName()); + connectionURL.append("&password="); + connectionURL.append(dbSettings.getPassword()); + + connectionPool.setUrl(connectionURL.toString()); + + // tweak pool configuration + connectionPool.setInitialSize(5); // start with 5 connections + connectionPool.setMaxIdle(CONN_POOL_SIZE); // max of 10 idle connections + connectionPool.setValidationQuery(dbSettings.getValidationQuery()); + } + + /** + * Initialize the database schema. + * + * Requires valid connectionPool. + * + * This method is called from within connect(), so we cannot call connect() + * to get a connection. This method is called after setupConnectionPool(), + * so it is safe to assume that a valid connectionPool exists. The + * implementation of connect() is synchronized, so we can safely use the + * connectionPool object directly. + */ + @Override + protected void initializeDatabaseSchema() throws EamDbException { + // The "id" column is an alias for the built-in 64-bit int "rowid" column. + // It is autoincrementing by default and must be of type "integer primary key". + // We've omitted the autoincrement argument because we are not currently + // using the id value to search for specific rows, so we do not care + // if a rowid is re-used after an existing rows was previously deleted. + StringBuilder createOrganizationsTable = new StringBuilder(); + createOrganizationsTable.append("CREATE TABLE IF NOT EXISTS organizations ("); + createOrganizationsTable.append("id SERIAL PRIMARY KEY,"); + createOrganizationsTable.append("org_name character varying(50) NOT NULL,"); + createOrganizationsTable.append("poc_name character varying(50) NOT NULL,"); + createOrganizationsTable.append("poc_email character varying(50) NOT NULL,"); + createOrganizationsTable.append("poc_phone character varying(20) NOT NULL"); + createOrganizationsTable.append(")"); + + // TODO: The organizations will only have a small number of rows, so + // determine if an index is worthwhile. + StringBuilder createCasesTable = new StringBuilder(); + createCasesTable.append("CREATE TABLE IF NOT EXISTS cases ("); + createCasesTable.append("id SERIAL PRIMARY KEY,"); + createCasesTable.append("case_uid character varying(50) NOT NULL,"); + createCasesTable.append("org_id integer,"); + createCasesTable.append("case_name character varying(50) NOT NULL,"); + createCasesTable.append("creation_date character varying(30) NOT NULL,"); + createCasesTable.append("case_number character varying(20) NOT NULL,"); + createCasesTable.append("examiner_name character varying(50) NOT NULL,"); + createCasesTable.append("examiner_email character varying(50) NOT NULL,"); + createCasesTable.append("examiner_phone character varying(20) NOT NULL,"); + createCasesTable.append("notes character varying(400) NOT NULL,"); + createCasesTable.append("foreign key (org_id) references organizations(id) on update set null on delete set null"); + createCasesTable.append(")"); + + // TODO: when there are few cases in the cases table, these indices may not be worthwhile + String casesIdx1 = "CREATE INDEX IF NOT EXISTS cases_org_id ON cases (org_id)"; + String casesIdx2 = "CREATE INDEX IF NOT EXISTS cases_case_uid ON cases (case_uid)"; + + StringBuilder createDataSourcesTable = new StringBuilder(); + createDataSourcesTable.append("CREATE TABLE IF NOT EXISTS data_sources ("); + createDataSourcesTable.append("id SERIAL PRIMARY KEY,"); + createDataSourcesTable.append("device_id character varying(50) NOT NULL,"); + createDataSourcesTable.append("name character varying(50) NOT NULL,"); + createDataSourcesTable.append("CONSTRAINT device_id_unique UNIQUE (device_id)"); + createDataSourcesTable.append(")"); + + String dataSourceIdx1 = "CREATE INDEX IF NOT EXISTS data_sources_name ON data_sources (name)"; + + StringBuilder createGlobalReferenceSetsTable = new StringBuilder(); + createGlobalReferenceSetsTable.append("CREATE TABLE IF NOT EXISTS global_reference_sets ("); + createGlobalReferenceSetsTable.append("id SERIAL PRIMARY KEY,"); + createGlobalReferenceSetsTable.append("org_id integer,"); + createGlobalReferenceSetsTable.append("set_name character varying(100) NOT NULL,"); + createGlobalReferenceSetsTable.append("version character varying(20) NOT NULL,"); + createGlobalReferenceSetsTable.append("import_date character varying(30) NOT NULL,"); + createGlobalReferenceSetsTable.append("foreign key (org_id) references organizations(id) on update set null on delete set null"); + createGlobalReferenceSetsTable.append(")"); + + String globalReferenceSetsIdx1 = "CREATE INDEX IF NOT EXISTS global_reference_sets_org_id ON global_reference_sets (org_id)"; + + StringBuilder createGlobalFilesTable = new StringBuilder(); + createGlobalFilesTable.append("CREATE TABLE IF NOT EXISTS global_files ("); + createGlobalFilesTable.append("id SERIAL PRIMARY KEY,"); + createGlobalFilesTable.append("global_reference_set_id integer,"); + createGlobalFilesTable.append("value character varying(100) NOT NULL,"); + createGlobalFilesTable.append("known_status character varying(10) NOT NULL,"); + createGlobalFilesTable.append("comment character varying(400) NOT NULL,"); + createGlobalFilesTable.append("CONSTRAINT global_files_multi_unique UNIQUE (global_reference_set_id,value),"); + createGlobalFilesTable.append("foreign key (global_reference_set_id) references global_reference_sets(id) on update set null on delete set null"); + createGlobalFilesTable.append(")"); + + String globalFilesIdx1 = "CREATE INDEX IF NOT EXISTS global_files_value ON global_files (value)"; + String globalFilesIdx2 = "CREATE INDEX IF NOT EXISTS global_files_value_known_status ON global_files (value, known_status)"; + + StringBuilder createArtifactTypesTable = new StringBuilder(); + createArtifactTypesTable.append("CREATE TABLE IF NOT EXISTS artifact_types ("); + createArtifactTypesTable.append("id SERIAL PRIMARY KEY,"); + createArtifactTypesTable.append("name character varying(20) NOT NULL,"); + createArtifactTypesTable.append("supported integer NOT NULL,"); + createArtifactTypesTable.append("enabled integer NOT NULL,"); + createArtifactTypesTable.append("CONSTRAINT artifact_type_name_unique UNIQUE (name)"); + createArtifactTypesTable.append(")"); + + // NOTE: there are API methods that query by one of: name, supported, or enabled. + // Only name is currently implemented, but, there will only be a small number + // of artifact_types, so there is no benefit to having any indices. + StringBuilder createArtifactInstancesTableTemplate = new StringBuilder(); + createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s_instances ("); + createArtifactInstancesTableTemplate.append("id SERIAL PRIMARY KEY,"); + createArtifactInstancesTableTemplate.append("case_id integer,"); + createArtifactInstancesTableTemplate.append("data_source_id integer,"); + createArtifactInstancesTableTemplate.append("value character varying(100) NOT NULL,"); + createArtifactInstancesTableTemplate.append("file_path character varying(256) NOT NULL,"); + createArtifactInstancesTableTemplate.append("known_status character varying(10) NOT NULL,"); + createArtifactInstancesTableTemplate.append("comment character varying(400) NOT NULL,"); + createArtifactInstancesTableTemplate.append("CONSTRAINT %s_instances_multi_unique_ UNIQUE (case_id, data_source_id, value, file_path),"); + createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) on update set null on delete set null,"); + createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) on update set null on delete set null"); + createArtifactInstancesTableTemplate.append(")"); + + // TODO: do we need any more indices? + String instancesIdx1 = "CREATE INDEX IF NOT EXISTS %s_instances_case_id ON %s_instances (case_id)"; + String instancesIdx2 = "CREATE INDEX IF NOT EXISTS %s_instances_data_source_id ON %s_instances (data_source_id)"; + String instancesIdx3 = "CREATE INDEX IF NOT EXISTS %s_instances_value ON %s_instances (value)"; + String instancesIdx4 = "CREATE INDEX IF NOT EXISTS %s_instances_value_known_status ON %s_instances (value, known_status)"; + + StringBuilder createDbInfoTable = new StringBuilder(); + createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); + createDbInfoTable.append("id SERIAL PRIMARY KEY NOT NULL,"); + createDbInfoTable.append("name character varying(50) NOT NULL,"); + createDbInfoTable.append("value character varying(50) NOT NULL"); + createDbInfoTable.append(")"); + + // NOTE: the db_info table currenly only has 1 row, so having an index + // provides no benefit. + Connection conn = null; + + try { + conn = connectionPool.getConnection(); + Statement stmt = conn.createStatement(); + + stmt.execute(createOrganizationsTable.toString()); + + stmt.execute(createCasesTable.toString()); + stmt.execute(casesIdx1); + stmt.execute(casesIdx2); + + stmt.execute(createDataSourcesTable.toString()); + stmt.execute(dataSourceIdx1); + + stmt.execute(createGlobalReferenceSetsTable.toString()); + stmt.execute(globalReferenceSetsIdx1); + + stmt.execute(createGlobalFilesTable.toString()); + stmt.execute(globalFilesIdx1); + stmt.execute(globalFilesIdx2); + + stmt.execute(createArtifactTypesTable.toString()); + + stmt.execute(createDbInfoTable.toString()); + + // Create a separate table for each artifact type + String type_name; + for (EamArtifact.Type type : DEFAULT_ARTIFACT_TYPES) { + type_name = type.getName(); + stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), type_name, type_name)); + stmt.execute(String.format(instancesIdx1, type_name, type_name)); + stmt.execute(String.format(instancesIdx2, type_name, type_name)); + stmt.execute(String.format(instancesIdx3, type_name, type_name)); + stmt.execute(String.format(instancesIdx4, type_name, type_name)); + } + } catch (SQLException ex) { + throw new EamDbException("Error initializing db schema.", ex); // NON-NLS + } finally { + closeConnection(conn); + } + } + + /** + * Lazily setup Singleton connection on first request. + * + * @return A connection from the connection pool. + * + * @throws EamDbException + */ + @Override + protected Connection connect() throws EamDbException { + synchronized (this) { + if (!dbSettings.isEnabled()) { + throw new EamDbException("Enterprise artifact manager is not enabled"); // NON-NLS + } + + if (connectionPool == null) { + setupConnectionPool(); + confirmDatabaseSchema(); + } + } + + try { + return connectionPool.getConnection(); + } catch (SQLException ex) { + throw new EamDbException("Error getting connection from connection pool.", ex); // NON-NLS + } + } + + @Override + public boolean isEnabled() { + return dbSettings.isEnabled(); + } + + @Override + public List getBadTags() { + return dbSettings.getBadTags(); + } + + @Override + public void setBadTags(List badTags) { + dbSettings.setBadTags(badTags); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/PostgresEamDbSettings.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/PostgresEamDbSettings.java new file mode 100644 index 0000000000..e1317d250d --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/PostgresEamDbSettings.java @@ -0,0 +1,359 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; + +/** + * Settings for the Postgres implementation of the enterprise artifact manager + * database + */ +public final class PostgresEamDbSettings { + + private final static Logger LOGGER = Logger.getLogger(PostgresEamDbSettings.class.getName()); + private final String DEFAULT_HOST = "localhost"; // NON-NLS + private final int DEFAULT_PORT = 5432; + private final String DEFAULT_DBNAME = "enterpriseartifactmanagerdb"; // NON-NLS + private final int DEFAULT_BULK_THRESHHOLD = 1000; + private final String DEFAULT_USERNAME = ""; + private final String DEFAULT_PASSWORD = ""; + private final String DEFAULT_BAD_TAGS = "Evidence"; // NON-NLS + private final String VALIDATION_QUERY = "SELECT version()"; // NON-NLS + private final String JDBC_BASE_URI = "jdbc:postgresql://"; // NON-NLS + private final String JDBC_DRIVER = "org.postgresql.Driver"; // NON-NLS + + private boolean enabled; + private String host; + private int port; + private String dbName; + private int bulkThreshold; + private String userName; + private String password; + private List badTags; + + public PostgresEamDbSettings() { + loadSettings(); + } + + public void loadSettings() { + enabled = Boolean.valueOf(ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.enabled")); // NON-NLS + + host = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.host"); // NON-NLS + if (host == null || host.isEmpty()) { + host = DEFAULT_HOST; + } + + try { + String portString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.port"); // NON-NLS + if (portString == null || portString.isEmpty()) { + port = DEFAULT_PORT; + } else { + port = Integer.parseInt(portString); + if (port < 0 || port > 65535) { + port = DEFAULT_PORT; + } + } + } catch (NumberFormatException ex) { + port = DEFAULT_PORT; + } + + dbName = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.dbName"); // NON-NLS + if (dbName == null || dbName.isEmpty()) { + dbName = DEFAULT_DBNAME; + } + + try { + String bulkThresholdString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.bulkThreshold"); // NON-NLS + if (bulkThresholdString == null || bulkThresholdString.isEmpty()) { + this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + } else { + this.bulkThreshold = Integer.parseInt(bulkThresholdString); + if (getBulkThreshold() <= 0) { + this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + } + } + } catch (NumberFormatException ex) { + this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + } + + userName = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.user"); // NON-NLS + if (userName == null || userName.isEmpty()) { + userName = DEFAULT_USERNAME; + } + + password = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.password"); // NON-NLS + if (password == null || password.isEmpty()) { + password = DEFAULT_PASSWORD; + } + + String badTagsStr = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.badTags"); // NON-NLS + if (badTagsStr == null || badTagsStr.isEmpty()) { + badTagsStr = DEFAULT_BAD_TAGS; + } + badTags = new ArrayList<>(Arrays.asList(badTagsStr.split(","))); + } + + public void saveSettings() { + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.enabled", Boolean.toString(isEnabled())); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.postgresql.host", getHost()); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.postgresql.port", Integer.toString(port)); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.postgresql.dbName", getDbName()); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.postgresql.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.postgresql.user", getUserName()); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.postgresql.password", getPassword()); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.badTags", String.join(",", badTags)); // NON-NLS + } + + /** + * Get the full connection URL as a String + * + * @return + */ + public String getConnectionURL() { + StringBuilder url = new StringBuilder(); + url.append(getJDBCBaseURI()); + url.append(getHost()); + url.append("/"); // NON-NLS + url.append(getDbName()); + url.append("?user="); // NON-NLS + url.append(getUserName()); + url.append("&password="); // NON-NLS + url.append(getPassword()); + + return url.toString(); + } + + public boolean testSettings() { + // Open a new ephemeral client here to test that we can connect + ResultSet resultSet = null; + Connection conn = null; + try { + String url = getConnectionURL(); + Class.forName(getDriver()); + conn = DriverManager.getConnection(url); + Statement tester = conn.createStatement(); + resultSet = tester.executeQuery(getValidationQuery()); + if (resultSet.next()) { + LOGGER.log(Level.INFO, "Testing connection to postgresql success."); // NON-NLS + } + } catch (ClassNotFoundException | SQLException ex) { + LOGGER.log(Level.INFO, "Testing connection to postgresql failed.", ex); // NON-NLS + return false; + } finally { + if (null != resultSet) { + try { + resultSet.close(); + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error closing ResultSet.", ex); // NON-NLS + } + } + if (null != conn) { + try { + conn.close(); + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error closing test connection.", ex); // NON-NLS + } + } + } + + return true; + } + + public boolean isChanged() { + String hostString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.host"); // NON-NLS + String portString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.port"); // NON-NLS + String dbNameString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.dbName"); // NON-NLS + String bulkThresholdString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.bulkThreshold"); // NON-NLS + String userNameString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.user"); // NON-NLS + String userPasswordString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.postgresql.password"); // NON-NLS + + return !host.equals(hostString) || !Integer.toString(port).equals(portString) + || !dbName.equals(dbNameString) || !Integer.toString(bulkThreshold).equals(bulkThresholdString) + || !userName.equals(userNameString) || !password.equals(userPasswordString); + } + + /** + * @return the enabled + */ + public boolean isEnabled() { + return enabled; + } + + /** + * @param enabled the enabled to set + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * @return the host + */ + public String getHost() { + return host; + } + + /** + * @param host the host to set + */ + public void setHost(String host) throws EamDbException { + if (null != host && !host.isEmpty()) { + this.host = host; + } else { + throw new EamDbException("Error invalid host for database connection. Cannot be null or empty."); // NON-NLS + } + } + + /** + * @return the port + */ + public int getPort() { + return port; + } + + /** + * @param port the port to set + */ + public void setPort(int port) throws EamDbException { + if (port > 0 && port < 65535) { + this.port = port; + } else { + throw new EamDbException("Error invalid port for database connection."); // NON-NLS + } + } + + /** + * @return the dbName + */ + public String getDbName() { + return dbName; + } + + /** + * @param dbName the dbName to set + */ + public void setDbName(String dbName) throws EamDbException { + if (dbName != null && !dbName.isEmpty()) { + this.dbName = dbName; + } else { + throw new EamDbException("Error invalid name for database connection. Cannot be null or empty."); // NON-NLS + + } + } + + /** + * @return the bulkThreshold + */ + public int getBulkThreshold() { + return bulkThreshold; + } + + /** + * @param bulkThreshold the bulkThreshold to set + */ + public void setBulkThreshold(int bulkThreshold) throws EamDbException { + if (bulkThreshold > 0) { + this.bulkThreshold = bulkThreshold; + } else { + throw new EamDbException("Error invalid bulk threshold for database connection."); // NON-NLS + } + } + + /** + * @return the userName + */ + public String getUserName() { + return userName; + } + + /** + * @param userName the userName to set + */ + public void setUserName(String userName) throws EamDbException { + if (userName != null && !userName.isEmpty()) { + this.userName = userName; + } else { + throw new EamDbException("Error invalid user name for database connection. Cannot be null or empty."); // NON-NLS + } + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @param password the password to set + */ + public void setPassword(String password) throws EamDbException { + if (password != null && !password.isEmpty()) { + this.password = password; + } else { + throw new EamDbException("Error invalid user password for database connection. Cannot be null or empty."); // NON-NLS + } + } + + /** + * @return the badTags + */ + public List getBadTags() { + return badTags; + } + + /** + * @param badTags the badTags to set + */ + public void setBadTags(List badTags) { + this.badTags = badTags; + } + + /** + * @return the VALIDATION_QUERY + */ + public String getValidationQuery() { + return VALIDATION_QUERY; + } + + /** + * @return the POSTGRES_DRIVER + */ + public String getDriver() { + return JDBC_DRIVER; + } + + /** + * @return the JDBC_BASE_URI + */ + public String getJDBCBaseURI() { + return JDBC_BASE_URI; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/SqliteEamDb.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/SqliteEamDb.java new file mode 100644 index 0000000000..ed8d117a19 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/SqliteEamDb.java @@ -0,0 +1,370 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import java.util.logging.Level; +import org.apache.commons.dbcp2.BasicDataSource; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Sqlite implementation of the enterprise artifact manager database + */ +public class SqliteEamDb extends AbstractSqlEamDb { + + private final static Logger LOGGER = Logger.getLogger(SqliteEamDb.class.getName()); + + private static SqliteEamDb instance; + + protected static final String PRAGMA_SYNC_OFF = "PRAGMA synchronous = OFF"; + protected static final String PRAGMA_SYNC_NORMAL = "PRAGMA synchronous = NORMAL"; + private static final String PRAGMA_JOURNAL_WAL = "PRAGMA journal_mode = WAL"; + private static final String PRAGMA_READ_UNCOMMITTED_TRUE = "PRAGMA read_uncommitted = True"; + private static final String PRAGMA_ENCODING_UTF8 = "PRAGMA encoding = 'UTF-8'"; + private static final String PRAGMA_PAGE_SIZE_4096 = "PRAGMA page_size = 4096"; + private static final String PRAGMA_FOREIGN_KEYS_ON = "PRAGMA foreign_keys = ON"; + private BasicDataSource connectionPool = null; + + private final SqliteEamDbSettings dbSettings; + + public synchronized static SqliteEamDb getInstance() { + if (instance == null) { + instance = new SqliteEamDb(); + } + + return instance; + } + + private SqliteEamDb() { + dbSettings = new SqliteEamDbSettings(); + updateSettings(); + } + + @Override + public void updateSettings() { + synchronized (this) { + dbSettings.loadSettings(); + bulkArtifactsThreshold = dbSettings.getBulkThreshold(); + } + } + + @Override + public void saveSettings() { + synchronized (this) { + dbSettings.saveSettings(); + } + } + + @Override + public void reset() throws EamDbException { + Connection conn = connect(); + + try { + Statement dropContent = conn.createStatement(); + dropContent.executeUpdate("DELETE FROM organizations"); + dropContent.executeUpdate("DELETE FROM cases"); + dropContent.executeUpdate("DELETE FROM data_sources"); + dropContent.executeUpdate("DELETE FROM global_reference_sets"); + dropContent.executeUpdate("DELETE FROM global_files"); + dropContent.executeUpdate("DELETE FROM artifact_types"); + dropContent.executeUpdate("DELETE FROM db_info"); + + String instancesTemplate = "DELETE FROM %s_instances"; + for (EamArtifact.Type type : DEFAULT_ARTIFACT_TYPES) { + dropContent.executeUpdate(String.format(instancesTemplate, type.getName().toLowerCase())); + } + + dropContent.executeUpdate("VACUUM"); + insertDefaultContent(); + + } catch (SQLException ex) { + LOGGER.log(Level.WARNING, "Failed to reset database.", ex); + } finally { + closeConnection(conn); + } + } + + /** + * Setup a connection pool for db connections. + * + */ + private void setupConnectionPool() throws EamDbException { + connectionPool = new BasicDataSource(); + connectionPool.setDriverClassName(dbSettings.getDriver()); + + StringBuilder connectionURL = new StringBuilder(); + connectionURL.append(dbSettings.getJDBCBaseURI()); + connectionURL.append(dbSettings.getDbDirectory()); + connectionURL.append(File.separator); + connectionURL.append(dbSettings.getDbName()); + + connectionPool.setUrl(connectionURL.toString()); + + // tweak pool configuration + connectionPool.setInitialSize(50); + connectionPool.setMaxTotal(-1); + connectionPool.setMaxIdle(-1); + connectionPool.setMaxWaitMillis(1000); + connectionPool.setValidationQuery(dbSettings.getValidationQuery()); + } + + /** + * Verify the CDB directory exists. If it doesn't, then create it. + * + * @throws EamDbException + */ + private void verifyCDBDirectory() throws EamDbException { + File dbDir = new File(dbSettings.getDbDirectory()); + if (!dbDir.exists()) { + LOGGER.log(Level.INFO, "sqlite directory does not exist, creating it at {0}.", dbSettings.getDbDirectory()); // NON-NLS + try { + Files.createDirectories(dbDir.toPath()); + } catch (IOException ex) { + throw new EamDbException("Failed to create sqlite database directory. ", ex); // NON-NLS + } + } else if (dbDir.exists() && !dbDir.isDirectory()) { + LOGGER.log(Level.INFO, "Failed to create sqlite database directory. Path already exists and is not a directory: {0}", dbSettings.getDbDirectory()); // NON-NLS + } + + } + + /** + * Initialize the database schema. + * + * Requires valid connectionPool. + * + * This method is called from within connect(), so we cannot call connect() + * to get a connection. This method is called after setupConnectionPool(), + * so it is safe to assume that a valid connectionPool exists. The + * implementation of connect() is synchronized, so we can safely use the + * connectionPool object directly. + */ + @Override + protected void initializeDatabaseSchema() throws EamDbException { + // The "id" column is an alias for the built-in 64-bit int "rowid" column. + // It is autoincrementing by default and must be of type "integer primary key". + // We've omitted the autoincrement argument because we are not currently + // using the id value to search for specific rows, so we do not care + // if a rowid is re-used after an existing rows was previously deleted. + StringBuilder createOrganizationsTable = new StringBuilder(); + createOrganizationsTable.append("CREATE TABLE IF NOT EXISTS organizations ("); + createOrganizationsTable.append("id integer primary key autoincrement NOT NULL,"); + createOrganizationsTable.append("org_name character varying(50) NOT NULL,"); + createOrganizationsTable.append("poc_name character varying(50) NOT NULL,"); + createOrganizationsTable.append("poc_email character varying(50) NOT NULL,"); + createOrganizationsTable.append("poc_phone character varying(20) NOT NULL"); + createOrganizationsTable.append(")"); + + // TODO: The organizations will only have a small number of rows, so + // determine if an index is worthwhile. + StringBuilder createCasesTable = new StringBuilder(); + createCasesTable.append("CREATE TABLE IF NOT EXISTS cases ("); + createCasesTable.append("id integer primary key autoincrement NOT NULL,"); + createCasesTable.append("case_uid character varying(50) NOT NULL,"); + createCasesTable.append("org_id integer,"); + createCasesTable.append("case_name character varying(50) NOT NULL,"); + createCasesTable.append("creation_date character varying(30) NOT NULL,"); + createCasesTable.append("case_number character varying(20) NOT NULL,"); + createCasesTable.append("examiner_name character varying(50) NOT NULL,"); + createCasesTable.append("examiner_email character varying(50) NOT NULL,"); + createCasesTable.append("examiner_phone character varying(20) NOT NULL,"); + createCasesTable.append("notes character varying(400) NOT NULL,"); + createCasesTable.append("foreign key (org_id) references organizations(id) on update set null on delete set null,"); + createCasesTable.append("CONSTRAINT case_uid_unique UNIQUE(case_uid)"); + createCasesTable.append(")"); + + // TODO: when there are few cases in the cases table, these indices may not be worthwhile + String casesIdx1 = "CREATE INDEX IF NOT EXISTS cases_org_id ON cases (org_id)"; + String casesIdx2 = "CREATE INDEX IF NOT EXISTS cases_case_uid ON cases (case_uid)"; + + StringBuilder createDataSourcesTable = new StringBuilder(); + createDataSourcesTable.append("CREATE TABLE IF NOT EXISTS data_sources ("); + createDataSourcesTable.append("id integer primary key autoincrement NOT NULL,"); + createDataSourcesTable.append("device_id character varying(50) NOT NULL,"); + createDataSourcesTable.append("name character varying(50) NOT NULL,"); + createDataSourcesTable.append("CONSTRAINT device_id_unique UNIQUE(device_id)"); + createDataSourcesTable.append(")"); + + String dataSourceIdx1 = "CREATE INDEX IF NOT EXISTS data_sources_name ON data_sources (name)"; + + StringBuilder createGlobalReferenceSetsTable = new StringBuilder(); + createGlobalReferenceSetsTable.append("CREATE TABLE IF NOT EXISTS global_reference_sets ("); + createGlobalReferenceSetsTable.append("id integer primary key autoincrement NOT NULL,"); + createGlobalReferenceSetsTable.append("org_id integer,"); + createGlobalReferenceSetsTable.append("set_name character varying(100) NOT NULL,"); + createGlobalReferenceSetsTable.append("version character varying(20) NOT NULL,"); + createGlobalReferenceSetsTable.append("import_date character varying(30) NOT NULL,"); + createGlobalReferenceSetsTable.append("foreign key (org_id) references organizations(id) on update set null on delete set null"); + createGlobalReferenceSetsTable.append(")"); + + String globalReferenceSetsIdx1 = "CREATE INDEX IF NOT EXISTS global_reference_sets_org_id ON global_reference_sets (org_id)"; + + StringBuilder createGlobalFilesTable = new StringBuilder(); + createGlobalFilesTable.append("CREATE TABLE IF NOT EXISTS global_files ("); + createGlobalFilesTable.append("id integer primary key autoincrement NOT NULL,"); + createGlobalFilesTable.append("global_reference_set_id integer,"); + createGlobalFilesTable.append("value character varying(100) NOT NULL,"); + createGlobalFilesTable.append("known_status character varying(10) NOT NULL,"); + createGlobalFilesTable.append("comment character varying(400) NOT NULL,"); + createGlobalFilesTable.append("CONSTRAINT global_files_multi_unique UNIQUE(global_reference_set_id, value)"); + createGlobalFilesTable.append("foreign key (global_reference_set_id) references global_reference_sets(id) on update set null on delete set null"); + createGlobalFilesTable.append(")"); + + String globalFilesIdx1 = "CREATE INDEX IF NOT EXISTS global_files_value ON global_files (value)"; + String globalFilesIdx2 = "CREATE INDEX IF NOT EXISTS global_files_value_known_status ON global_files (value, known_status)"; + + StringBuilder createArtifactTypesTable = new StringBuilder(); + createArtifactTypesTable.append("CREATE TABLE IF NOT EXISTS artifact_types ("); + createArtifactTypesTable.append("id integer primary key autoincrement NOT NULL,"); + createArtifactTypesTable.append("name character varying(20) NOT NULL,"); + createArtifactTypesTable.append("supported integer NOT NULL,"); + createArtifactTypesTable.append("enabled integer NOT NULL,"); + createArtifactTypesTable.append("CONSTRAINT artifact_type_name_unique UNIQUE (name)"); + createArtifactTypesTable.append(")"); + + // NOTE: there are API methods that query by one of: name, supported, or enabled. + // Only name is currently implemented, but, there will only be a small number + // of artifact_types, so there is no benefit to having any indices. + StringBuilder createArtifactInstancesTableTemplate = new StringBuilder(); + createArtifactInstancesTableTemplate.append("CREATE TABLE IF NOT EXISTS %s_instances ("); + createArtifactInstancesTableTemplate.append("id integer primary key autoincrement NOT NULL,"); + createArtifactInstancesTableTemplate.append("case_id integer,"); + createArtifactInstancesTableTemplate.append("data_source_id integer,"); + createArtifactInstancesTableTemplate.append("value character varying(100) NOT NULL,"); + createArtifactInstancesTableTemplate.append("file_path character varying(256) NOT NULL,"); + createArtifactInstancesTableTemplate.append("known_status character varying(10) NOT NULL,"); + createArtifactInstancesTableTemplate.append("comment character varying(400) NOT NULL,"); + createArtifactInstancesTableTemplate.append("CONSTRAINT %s_instances_multi_unique UNIQUE(case_id, data_source_id, value, file_path),"); + createArtifactInstancesTableTemplate.append("foreign key (case_id) references cases(id) on update set null on delete set null,"); + createArtifactInstancesTableTemplate.append("foreign key (data_source_id) references data_sources(id) on update set null on delete set null"); + createArtifactInstancesTableTemplate.append(")"); + + // TODO: do we need any more indices? + String instancesIdx1 = "CREATE INDEX IF NOT EXISTS %s_instances_case_id ON %s_instances (case_id)"; + String instancesIdx2 = "CREATE INDEX IF NOT EXISTS %s_instances_data_source_id ON %s_instances (data_source_id)"; + String instancesIdx3 = "CREATE INDEX IF NOT EXISTS %s_instances_value ON %s_instances (value)"; + String instancesIdx4 = "CREATE INDEX IF NOT EXISTS %s_instances_value_known_status ON %s_instances (value, known_status)"; + + StringBuilder createDbInfoTable = new StringBuilder(); + createDbInfoTable.append("CREATE TABLE IF NOT EXISTS db_info ("); + createDbInfoTable.append("id integer primary key NOT NULL,"); + createDbInfoTable.append("name character varying(50) NOT NULL,"); + createDbInfoTable.append("value character varying(50) NOT NULL"); + createDbInfoTable.append(")"); + + // NOTE: the db_info table currenly only has 1 row, so having an index + // provides no benefit. + Connection conn = null; + try { + conn = connectionPool.getConnection(); + Statement stmt = conn.createStatement(); + stmt.execute(PRAGMA_JOURNAL_WAL); + stmt.execute(PRAGMA_SYNC_OFF); + stmt.execute(PRAGMA_READ_UNCOMMITTED_TRUE); + stmt.execute(PRAGMA_ENCODING_UTF8); + stmt.execute(PRAGMA_PAGE_SIZE_4096); + stmt.execute(PRAGMA_FOREIGN_KEYS_ON); + + stmt.execute(createOrganizationsTable.toString()); + + stmt.execute(createCasesTable.toString()); + stmt.execute(casesIdx1); + stmt.execute(casesIdx2); + + stmt.execute(createDataSourcesTable.toString()); + stmt.execute(dataSourceIdx1); + + stmt.execute(createGlobalReferenceSetsTable.toString()); + stmt.execute(globalReferenceSetsIdx1); + + stmt.execute(createGlobalFilesTable.toString()); + stmt.execute(globalFilesIdx1); + stmt.execute(globalFilesIdx2); + + stmt.execute(createArtifactTypesTable.toString()); + + stmt.execute(createDbInfoTable.toString()); + + // Create a separate table for each artifact type + String type_name; + for (EamArtifact.Type type : DEFAULT_ARTIFACT_TYPES) { + type_name = type.getName(); + stmt.execute(String.format(createArtifactInstancesTableTemplate.toString(), type_name, type_name)); + stmt.execute(String.format(instancesIdx1, type_name, type_name)); + stmt.execute(String.format(instancesIdx2, type_name, type_name)); + stmt.execute(String.format(instancesIdx3, type_name, type_name)); + stmt.execute(String.format(instancesIdx4, type_name, type_name)); + } + } catch (SQLException ex) { + throw new EamDbException("Error initializing db schema.", ex); // NON-NLS + } finally { + closeConnection(conn); + } + } + + /** + * Lazily setup Singleton connection on first request. + * + * @return A connection from the connection pool. + * + * @throws EamDbException + */ + @Override + protected Connection connect() throws EamDbException { + synchronized (this) { + if (!dbSettings.isEnabled()) { + throw new EamDbException("Enterprise artifact manager is not enabled"); // NON-NLS + } + + if (connectionPool == null) { + verifyCDBDirectory(); + setupConnectionPool(); + confirmDatabaseSchema(); + } + + try { + return connectionPool.getConnection(); + } catch (SQLException ex) { + throw new EamDbException("Error getting connection from connection pool.", ex); // NON-NLS + } + } + } + + @Override + public boolean isEnabled() { + return dbSettings.isEnabled(); + } + + @Override + public List getBadTags() { + return dbSettings.getBadTags(); + } + + @Override + public void setBadTags(List badTags) { + dbSettings.setBadTags(badTags); + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/SqliteEamDbSettings.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/SqliteEamDbSettings.java new file mode 100644 index 0000000000..2168c72e72 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/datamodel/SqliteEamDbSettings.java @@ -0,0 +1,304 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; + +/** + * Settings for the sqlite implementation of the enterprise artifact manager database + */ +public final class SqliteEamDbSettings { + + private final static Logger LOGGER = Logger.getLogger(SqliteEamDbSettings.class.getName()); + private final String DEFAULT_DBNAME = "enterpriseartifactmanagerdb.db"; // NON-NLS + private final String DEFAULT_DBDIRECTORY = System.getProperty("user.home") + File.separator + "Autopsy" + File.separator + "cdb"; // NON-NLS + private final int DEFAULT_BULK_THRESHHOLD = 1000; + private final String DEFAULT_BAD_TAGS = "Evidence"; // NON-NLS + private final String JDBC_DRIVER = "org.sqlite.JDBC"; // NON-NLS + private final String JDBC_BASE_URI = "jdbc:sqlite:"; // NON-NLS + private final String VALIDATION_QUERY = "SELECT count(*) from sqlite_master"; // NON-NLS + + private boolean enabled; + private String dbName; + private String dbDirectory; + private int bulkThreshold; + private List badTags; + + public SqliteEamDbSettings() { + loadSettings(); + } + + public void loadSettings() { + enabled = Boolean.valueOf(ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.enabled")); // NON-NLS + + dbName = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.sqlite.dbName"); // NON-NLS + if (dbName == null || dbName.isEmpty()) { + dbName = DEFAULT_DBNAME; + } + + dbDirectory = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.sqlite.dbDirectory"); // NON-NLS + if (dbDirectory == null || dbDirectory.isEmpty()) { + dbDirectory = DEFAULT_DBDIRECTORY; + } + + try { + String bulkThresholdString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.sqlite.bulkThreshold"); // NON-NLS + if (bulkThresholdString == null || bulkThresholdString.isEmpty()) { + this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + } else { + this.bulkThreshold = Integer.parseInt(bulkThresholdString); + if (getBulkThreshold() <= 0) { + this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + } + } + } catch (NumberFormatException ex) { + this.bulkThreshold = DEFAULT_BULK_THRESHHOLD; + } + + String badTagsStr = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.badTags"); // NON-NLS + if (badTagsStr == null || badTagsStr.isEmpty()) { + badTagsStr = DEFAULT_BAD_TAGS; + } + badTags = new ArrayList<>(Arrays.asList(badTagsStr.split(","))); + } + + public void saveSettings() { + createAndVerifyDirectory(); + + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.enabled", Boolean.toString(isEnabled())); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.sqlite.dbName", getDbName()); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.sqlite.dbDirectory", getDbDirectory()); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.sqlite.bulkThreshold", Integer.toString(getBulkThreshold())); // NON-NLS + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.badTags", String.join(",", badTags)); // NON-NLS + } + + public boolean createAndVerifyDirectory() { + // Ensure dbDirectory is a valid directory + File dbDir = new File(getDbDirectory()); + if (!dbDir.exists()) { + LOGGER.log(Level.INFO, "sqlite directory does not exist, creating it at {0}.", getDbDirectory()); // NON-NLS + try { + Files.createDirectories(dbDir.toPath()); + } catch (IOException ex) { + LOGGER.log(Level.INFO, "Failed to create sqlite database directory.", ex); // NON-NLS + return false; + } + } else if (dbDir.exists() && !dbDir.isDirectory()) { + LOGGER.log(Level.INFO, "Failed to create sqlite database directory. Path already exists and is not a directory."); // NON-NLS + return false; + } + + return true; + } + + /** + * Get the full connection URL as a String + * + * @return + */ + public String getConnectionURL() { + StringBuilder url = new StringBuilder(); + url.append(getJDBCBaseURI()); + url.append(getFileNameWithPath()); + + return url.toString(); + } + + public boolean testSettings() { + if (!createAndVerifyDirectory()) { + return false; + } + + // Open a new ephemeral client here to test that we can connect + ResultSet resultSet = null; + Connection conn = null; + try { + String url = getConnectionURL(); + Class.forName(getDriver()); + conn = DriverManager.getConnection(url); + Statement tester = conn.createStatement(); + resultSet = tester.executeQuery(getValidationQuery()); + if (resultSet.next()) { + LOGGER.log(Level.INFO, "Testing connection to sqlite success."); // NON-NLS + } + } catch (ClassNotFoundException | SQLException ex) { + LOGGER.log(Level.INFO, "Testing connection to sqlite failed.", ex); // NON-NLS + return false; + } finally { + if (null != resultSet) { + try { + resultSet.close(); + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error closing ResultSet.", ex); // NON-NLS + } + } + if (null != conn) { + try { + conn.close(); + } catch (SQLException ex) { + LOGGER.log(Level.SEVERE, "Error closing test connection.", ex); // NON-NLS + } + } + } + + return true; + } + + public boolean isChanged() { + String dbNameString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.sqlite.dbName"); // NON-NLS + String dbDirectoryString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.sqlite.dbDirectory"); // NON-NLS + String bulkThresholdString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.sqlite.bulkThreshold"); // NON-NLS + + return !dbName.equals(dbNameString) + || !dbDirectory.equals(dbDirectoryString) + || !Integer.toString(bulkThreshold).equals(bulkThresholdString); + } + + /** + * @return the enabled + */ + public boolean isEnabled() { + return enabled; + } + + /** + * @param enabled the enabled to set + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * @return the dbName + */ + public String getDbName() { + return dbName; + } + + /** + * Name of the sqlite db file. + * + * @param dbName the dbName to set + */ + public void setDbName(String dbName) throws EamDbException { + if (dbName != null && !dbName.isEmpty()) { + this.dbName = dbName; + } else { + throw new EamDbException("Error invalid file name for database. Cannot be null or empty."); // NON-NLS + } + } + + /** + * @return the bulkThreshold + */ + public int getBulkThreshold() { + return bulkThreshold; + } + + /** + * @param bulkThreshold the bulkThreshold to set + */ + public void setBulkThreshold(int bulkThreshold) throws EamDbException { + if (bulkThreshold > 0) { + this.bulkThreshold = bulkThreshold; + } else { + throw new EamDbException("Error invalid bulk threshold for database connection."); // NON-NLS + } + } + + /** + * @return the badTags + */ + public List getBadTags() { + return badTags; + } + + /** + * @param badTags the badTags to set + */ + public void setBadTags(List badTags) { + this.badTags = badTags; + } + + /** + * @return the dbDirectory + */ + public String getDbDirectory() { + return dbDirectory; + } + + /** + * Path for directory to hold the sqlite database. + * + * User must have WRITE permission to this directory. + * + * @param dbDirectory the dbDirectory to set + */ + public void setDbDirectory(String dbDirectory) throws EamDbException { + if (dbDirectory != null && !dbDirectory.isEmpty()) { + this.dbDirectory = dbDirectory; + } else { + throw new EamDbException("Error invalid directory for sqlite database. Cannot be null or empty"); // NON-NLS + } + } + + /** + * Join the DbDirectory and the DbName into a full path. + * + * @return + */ + public String getFileNameWithPath() { + return getDbDirectory() + File.separator + getDbName(); + } + + /** + * @return the DRIVER + */ + public String getDriver() { + return JDBC_DRIVER; + } + + /** + * @return the VALIDATION_QUERY + */ + public String getValidationQuery() { + return VALIDATION_QUERY; + } + + /** + * @return the JDBC_BASE_URI + */ + public String getJDBCBaseURI() { + return JDBC_BASE_URI; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/CONFIG.md b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/CONFIG.md new file mode 100644 index 0000000000..d3bd10561e --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/CONFIG.md @@ -0,0 +1,140 @@ +# Configuration + +## Database Setup + +There are 2 choices for database platforms: SQLite and PostgreSQL. +1. SQLite is a database in a file stored locally on the same host that is running Autopsy. +There is nothing to do to setup this database. It will be created by Autopsy on your +behalf, if it doesn't already exist. +2. PostgreSQL is a database server that can be run either on the same host that is +running Autopsy or on a remote server. To use PostgreSQL with the EnterpriseArtifactManager module, +you will need the server to be running, have an existing database named "enterpriseartifactmanagerdb" +and have an existing user/pass with ownership of the enterpriseartifactmanagerdb database. +The tables and indices will be automatically created by Autopsy. +See the [Autopsy multi-user settings documentation[(http://sleuthkit.org/autopsy/docs/user-docs/4.3/install_postgresql.html) for help setting up your PostgreSQL server. + +## Enable Module and Configure Database Settings + +In the menu go to: Tools -> Options -> EnterpriseArtifactManager + +1. Check the box to Enable Enterprise Artifact Manager. This will enable the Database Platform dropdown and Configure button. +2. In the dropdown, select the database platform that you want to use. +3. Click the Configure button to configure the settings for the chosen database platform. +4. Click the Apply button to save your database configuration settings. + +### Configure SQLite Database Settings + +There is only one step here, to specify the path and filename for the database. +You can accept the default value or use the Open button to choose another path. +The database file name can be called anything you want, but it is convenient to +give it a ".db" suffix. + +Once you have selected the path, click the Test Connection button. +If you see a green check next to the button, everything is ready to go. +If you see a red check next to the button, there is a problem with the path +you selected and you'll have to resolve that problem. + +Once the test passes, click the Save button to save your selection and close the window. + +### Configure PostgreSQL Database Settings + +For PostgreSQL all values are required, but some defaults are provided for convenience. + +1. Host Name/IP is the hostname or IP of your PostgreSQL server. +2. Port is the port that the PostgreSQL server is listening on; default is 5432. +3. Database name is the name of the database you are using for this module; default is enterpriseartifactmanagerdb. +4. User Name is the PostgreSQL user that owns and has full permissions to the database specified in step 3. +5. User Password is the password for the user. + +Once all values have been entered, click the Test Connection button. +If you see a green check next to the button, everything is ready to go. +If you see a red check next to the button, there is a problem with the values +you entered and you'll have to resolve that problem. + +Once the test passes, click the Save button to save your selection and close the window. + +## Import Globally Known Artifacts + +The purpose of this feature is to store any Known or Known Bad Artifacts in +the database. Think of this feature like a dynamic Hash List. +These artifacts are used during Ingest to flag files as Interesting. +They are also displayed in the Content Viewer when a file or artifact is selected that is +associated with one of the globally known artifacts. + +When importing a hash database, all fields are required. + +1. Select the Database Path using the Open button. This is the file containing +the hash values that you want to import. You can import multiple files, but only +one at a time. The format of these files must be the same format as used by +the hash database module. +2. Select the database type. The type of content in the database being imported. +3. Define the attribution for this database. + a. Select the Source Organization in the dropdown list. +This is the organization that provided the hash database to you. + b. If you do not see the Organization in the list, use the [Add New Organization](FEATURES.md#adding-a-new-organization) button to add it. +Once you add it, you can then select it in the dropdown list. + c. Enter a name for the dataset. This can be anything you want, but is often something like "child porn", "drugs", "malware", "corp hashlist", etc. + d. Enter a version number for that dataset. This can be anything you want, but is often something like "1.0", "1.1a", 20170505", etc. +4. Click the OK button to start the import. + +## Manage Correlatable Tags + +In Autopsy, you are allowed to define your own Tag names, tag files and artifacts, + and add comments when you tag a file or artifact. + +The purpose of this feature is to associate one or more of those tags with this module +to be used for Correlation. +By default there is a tag called "Evidence" as the only tag associated with this module. + +To associate one or more tag(s) with this module, check the Correlate box next to the tag +name(s) and click OK. + +### What does it mean for a tag to be associated with this module? + +Any file or artifact that a user tags with one of the associated tags will be +added to the database as a file or artifact of interest. +Any future data source ingest, where this module is enabled, will use those +files or artifacts as if they were part of the Known Bad list, causing matching files +from that ingest to be added to the Interesting Artifacts list in that currently open case. + +The term Correlate means that files processed during a future ingest will be correlated +with files existing in the database. + +As an example, I have a case open and I tag an image called "evilphoto.png" with the +default "Evidence" tag. That image will be stored in the database as a file of interest. +In the next data source that I ingest for the same case or a future case, +if an image with the same MD5 hash as "evilphoto.png" +is found, it will be automatically added to the Interesting Files tree and assumed +to be evidence. +This makes it easy to find and flag things in future cases that you know are +Interesting. + +## Manage Correlation Types + +This feature allows the user to control how much data is being stored in the database +to use for correlation and analysis. +By default, only FILES is enabled. +Select the types that you want to enable and click OK. + +The meaning of each type is as follows: + +* FILES - file path and MD5 hash +* DOMAIN - domain name +* EMAIL - email address +* PHONE - phone number +* USBID - device ID of connected USB devices. + +### What does Correlation mean? + +Artifacts stored in the database are available for this module to use for analysis. +That analysis comes in many forms. +When a file or artifact is extracted during ingest, this module will use the database +to find other files or artifacts that match it, to determine if that new file should be +flagged as an Interesting File. + +If that file or artifact does not exist in the database, and that Correlation Type +is enabled, then it will be added to the database. + +Having more data in the database will obviously allow this module to be more thorough, +but for some, database size is a concern, so we allow them to select a subset of data +to collect and use for analysis. diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/DEVELOP.md b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/DEVELOP.md new file mode 100644 index 0000000000..5b61ce70c4 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/DEVELOP.md @@ -0,0 +1,61 @@ +# Instructions for doing development of Autopsy Modules + +## On Windows, Setup your development environment with Autopsy sources and javadocs + +* Install x64 PostgreSQL and setup: + * http://sleuthkit.org/autopsy/docs/user-docs/4.3/install_postgresql.html + +* Install Oracle Java SE JDK 8 - Windows x64 from Oracle: + * http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html + +* Install NetBeans (choose the 'All' version): + * https://netbeans.org/downloads/ + +* Install Git for Windows x64: + * https://git-scm.com/downloads + +* Install doxygen and make sure it is added to your PATH + * http://www.stack.nl/~dimitri/doxygen/download.html + +* Sleuthkit and the DataModel java bindings + * Clone sleuthkit repo and set TSK_HOME environment variable. + * For the java bindings, there are two ways to get these + 1. [build Sleuthkit and then the java bindings](https://github.com/sleuthkit/sleuthkit/blob/develop/win32/BUILDING.txt), requiring Visual Studio and several + dependant libraries. + 2. download the [Autopsy dev platform zip](https://github.com/sleuthkit/autopsy/releases/download/autopsy-4.4.0/autopsy-4.4.0-devplatform.zip) and copy autopsy-4.4.0-devplatform/autopsy/modules/ext/Tsk_DataModel_PostgreSQL.jar to TSK_HOME/bindings/java/dist/ + * Set up environment variables, sample values: + - JAVA_HOME=C:\Program Files\Java\jdk1.8.0_121 + - JDK_HOME=C:\Program Files\Java\jdk1.8.0_121 + - JRE_HOME_64=C:\Program Files\Java\jre1.8.0_121 + - LIBEWF_HOME=C:\libewf_64bit (only needed if you chose option #1 above) + - LIBVHDI_HOME=C:\libvhdi_64bit (only needed if you chose option #1 above) + - POSTGRESQL_HOME_64=c:\Program Files\PostgreSQL\9.6 (only needed if you chose option #1 above) + - TSK_HOME=c:\sleuthkit + - PATH=...;C:\Program Files\Java\jdk1.8.0_121\bin;C:\Program Files\NetBeans 8.2\extide\ant\bin;C:\Program Files\doxygen\bin + +* Build Autopsy platform: + * Reference: https://github.com/sleuthkit/autopsy/blob/develop/BUILDING.txt + * Clone Autopsy project + * git clone git@github.com:sleuthkit/autopsy.git + * git checkout develop + * Add Autopsy project to NetBeans + * File -> Open Project + * Build the top level Autopsy project + * Generate javadoc and add doc folder in the documentation tab + +If the project builds correctly, everything is installed correctly. + +## How to build disk images for development/testing + +Refer to MS technet instructions for creating/using a VHD: https://technet.microsoft.com/en-us/library/gg318052(v=ws.10).aspx + +But here is the general idea: +* On Windows, use Disk Management tool to create a Virtual Hard Disk (.vhd) using the "dynamically expanding" disk format. Choose a small-ish disk size if you want the testing to be quick. +* Initialize the disk (Initialize Disk). +* Format the disk (New Simple Volume). +* Mount that disk (Attach VHD) +* Copy some files onto the disk. +* Umount that disk (Detach VHD). Do NOT delete the disk when detaching! + +Repeat the above steps to create additional disk images. + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/FEATURES.md b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/FEATURES.md new file mode 100644 index 0000000000..de96fee0a0 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/docs/FEATURES.md @@ -0,0 +1,116 @@ +# Features + +Once you have configured everything, created a case, and have run the ingest of at least one data source, +you can make use of some other exciting features that are described below. + +## Content Viewer + +This module adds a new tab to the [Content Viewer](http://sleuthkit.org/autopsy/docs/user-docs/4.3/content_viewer_page.html). +The tab for this module is called "Other Cases". +It can display data that is found in other cases, other data sources for the same case, or imported global artifacts. + +If at least one other case or data source has been ingested with this module enabled, +there is a potential that data will be displayed in the Other Cases content viewer. +If the selected file or artifact is associated by one of the supported Correlation Types, +to one or more file(s) or artifact(s) in the database, the associated files/artifacts will be displayed. +Note: the Content Viewer will display ALL associated files and artifacts available in the database. +It ignores the user's [enabled/disabled Correlation Types](CONFIG.md#manage-correlation-types). + +If the user right-clicks on a row, a menu will be displayed. +This menu has several options. +1. [Show Commonality Details](FEATURES.md#show-commonality-details) +2. [Save to CSV](FEATURES.md#save-to-csv) +3. [Show Case Details](FEATURES.md#show-case-details) +4. [Select All](FEATURES.md#select-all) + +Click option for more details. + +### Rows in the table + +By default, the rows in the content viewer will have background colors to indicate if they +are known to be of interest. +Files/artifacts that are Known Bad will have a Red background, Unknown will have Yellow background, +and Known will have a White background. + +The user can click on any column heading to sort by the values in that column. + +### Show Commonality Details + +The concept of Commonality simply means, how common is the selected file. +The value is the percentage of case/data source tuples that have the selected file or artifact. + +### Save to CSV + +This option will save ALL SELECTED rows in the Content Viewer table to a CSV file. +By default, the CSV file is saved into the Export directory inside the currently open Autopsy case, +but the user is free to select a different location. + +Note: if you want to copy/paste rows, it is usually possible to use CTRL+C to copy the +selected rows and then CTRL+V to paste them into a file, but it will not be CSV formatted. + +### Show Case Details + +This option will open a dialog that displays all of the relevant details for the selected case. +The details will include: +1. Case UUID +2. Case Name +3. Case Creation Date +4. Case Examiner contact information +5. Case Examiner's notes + +These details would have been entered by the examiner of the selected case, by visiting +the Case -> Enterprise Artifact Manager Case Details menu, when that case was open. + +### Select All + +This option will select all rows in the Content Viewer table. + +## Interesting Items tree + +In the Results tree of an open case is an entry called Interesting Items. +When this module is enabled, all of the enabled Correlatable Types will cause +matching files to be added to this Interesting Items tree during ingest. + +As an example, if the FILES Correlatable Type is enabled, and the ingest is +currently processing a file, for example "badfile.exe", and the MD5 hash for that +file already exists in the database as a KNOWN BAD file, then an entry in the Interesting Items tree +will be added for the current instance of "badfile.exe" in the data source currently being ingested. + +The same type of thing will happen for each [enabled Correlatable Type](CONFIG.md#manage-correlation-types). + +In the case of the PHONE correlatable type, the Interesting Items tree will start +a sub-tree for each phone number. The sub-tree will then contain each instance of that +Known Bad phone number. + +## Edit Enterprise Artifact Manager Case Details + +By default, Autopsy lets you edit Case Details in the Case menu. +When this module is enabled, there is an additional option in the Case menu, +called "Enterprise Artifact Manager Case Details". + +This is where the examiner can store a number of details about the case. +1. The organization of the case examiner. +2. The contact information of the case examiner. +3. The case examiner's case notes. + +To define the organization of the case examiner, simply select the organization name +from the dropdown box. +If the organization is not listed, you can click [Add New Organization](FEATURES.md#adding-a-new-organization) button. +Once the new organization is added, it should be available in the dropdown box. + +## Adding a New Organization + +An Organization can have two purposes in this module. + +1. It defines the Organization that the forensic examiner belongs to. +This organization is selected or added when Editing Correlation Case Details. +2. It defines the Organization that is the source of a Globally Known Artifact List. +This organization is selected or added during Import of a Globally Known Artifact hash list. + +When adding a new organization, only the Organization Name is required. +It is recommended to also include a Point of Contact for that organization. +This will be someone that is a manager or team lead at that Organization that +could be contacted for any questions about a case or a shared Globally Known Artifact +hash list. + +Click OK to save the new Organization. diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/BadFileTagRunner.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/BadFileTagRunner.java new file mode 100644 index 0000000000..26c24db3b5 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/BadFileTagRunner.java @@ -0,0 +1,56 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.eventlisteners; + +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Thread to send info to remote DB that tags a file as known bad. + */ +public class BadFileTagRunner implements Runnable { + + private static final Logger LOGGER = Logger.getLogger(BadFileTagRunner.class.getName()); + private static final long serialVersionUID = 1L; + + private final EamArtifact artifact; + private final EamDb dbManager; + + public BadFileTagRunner(EamArtifact artifact) { + this.artifact = artifact; + this.dbManager = EamDb.getInstance(); + } + + @Override + public void run() { + if (!dbManager.isEnabled()) { + LOGGER.log(Level.WARNING, "Enterprise artifact manager database not configured"); // NON-NLS + return; + } + + try { + dbManager.setArtifactInstanceKnownBad(this.artifact); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to enterprise artifact manager database.", ex); //NON-NLS + } + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/CaseEventListener.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/CaseEventListener.java new file mode 100644 index 0000000000..a78a6ad246 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/CaseEventListener.java @@ -0,0 +1,228 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.eventlisteners; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.logging.Level; +import java.util.stream.Collectors; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; +import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactInstance; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamCase; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDataSource; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamOrganization; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardArtifactTag; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.datamodel.TskDataException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Listen for case events and update entries in the enterprise artifact manager + * database accordingly + */ +@Messages({"caseeventlistener.evidencetag=Evidence"}) +public class CaseEventListener implements PropertyChangeListener { + + private static final Logger LOGGER = Logger.getLogger(CaseEventListener.class.getName()); + + @Override + public void propertyChange(PropertyChangeEvent evt) { + EamDb dbManager = EamDb.getInstance(); + switch (Case.Events.valueOf(evt.getPropertyName())) { + case CONTENT_TAG_ADDED: { + final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) evt; + final ContentTag tagAdded = tagAddedEvent.getAddedTag(); + // TODO: detect failed cast and break if so. + final AbstractFile af = (AbstractFile) tagAdded.getContent(); + final TagName tagName = tagAdded.getName(); + + if ((af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) + || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) + || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) + || (af.getKnown() == TskData.FileKnown.KNOWN) + || (af.isDir() == true)) { + break; + } + + String dsName; + try { + dsName = af.getDataSource().getName(); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error, unable to get name of data source from abstract file during CONTENT_TAG_ADDED event.", ex); + return; + } + + if (dbManager.getBadTags().contains(tagName.getDisplayName())) { + String md5 = af.getMd5Hash(); + if (md5 == null || md5.isEmpty()) { + return; + } + String deviceId = ""; + try { + deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(af.getDataSource().getId()).getDeviceId(); + } catch (TskCoreException | TskDataException ex) { + LOGGER.log(Level.SEVERE, "Error, failed to get deviceID or data source from current case.", ex); + } + + EamArtifact eamArtifact; + try { + EamArtifact.Type filesType = dbManager.getCorrelationArtifactTypeByName("FILES"); + eamArtifact = new EamArtifact(filesType, af.getMd5Hash()); + EamArtifactInstance cei = new EamArtifactInstance( + new EamCase(Case.getCurrentCase().getName(), Case.getCurrentCase().getDisplayName()), + new EamDataSource(deviceId, dsName), + af.getParentPath() + af.getName(), + tagAdded.getComment(), + EamArtifactInstance.KnownStatus.BAD, + EamArtifactInstance.GlobalStatus.LOCAL + ); + eamArtifact.addInstance(cei); + // send update to enterprise artifact manager db + Runnable r = new BadFileTagRunner(eamArtifact); + // TODO: send r into a thread pool instead + Thread t = new Thread(r); + t.start(); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error, unable to get correlation artifact type MD5 during CONTENT_TAG_ADDED event.", ex); + } + } + } // CONTENT_TAG_ADDED + break; + + case BLACKBOARD_ARTIFACT_TAG_ADDED: { + final BlackBoardArtifactTagAddedEvent bbTagAddedEvent = (BlackBoardArtifactTagAddedEvent) evt; + final BlackboardArtifactTag bbTagAdded = bbTagAddedEvent.getAddedTag(); + final AbstractFile af = (AbstractFile) bbTagAdded.getContent(); + final BlackboardArtifact bbArtifact = bbTagAdded.getArtifact(); + final TagName tagName = bbTagAdded.getName(); + + if (af.getKnown() == TskData.FileKnown.KNOWN) { + break; + } + + if (dbManager.getBadTags().contains(tagName.getDisplayName())) { + try { + EamArtifact eamArtifact = EamArtifactUtil.fromBlackboardArtifact(bbArtifact, true, dbManager.getCorrelationArtifactTypes(), true); + if (null != eamArtifact) { + eamArtifact.getInstances().get(0).setComment(bbTagAdded.getComment()); + Runnable r = new BadFileTagRunner(eamArtifact); + // TODO: send r into a thread pool instead + Thread t = new Thread(r); + t.start(); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error, unable to get correlation artifact types during BLACKBOARD_ARTIFACT_TAG_ADDED event.", ex); + } + } + } // BLACKBOARD_ARTIFACT_TAG_ADDED + break; + + case DATA_SOURCE_ADDED: { + final DataSourceAddedEvent dataSourceAddedEvent = (DataSourceAddedEvent) evt; + Content newDataSource = dataSourceAddedEvent.getDataSource(); + if (!dbManager.isEnabled()) { + break; + } + + try { + String deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(newDataSource.getId()).getDeviceId(); + + if (null == dbManager.getDataSourceDetails(deviceId)) { + dbManager.newDataSource(new EamDataSource(deviceId, newDataSource.getName())); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to enterprise artifact manager database.", ex); //NON-NLS + } catch (TskCoreException | TskDataException ex) { + LOGGER.log(Level.SEVERE, "Error getting data source from DATA_SOURCE_ADDED event content.", ex); //NON-NLS + } + } // DATA_SOURCE_ADDED + break; + + case CURRENT_CASE: { + /* + * A case has been opened if evt.getOldValue() is null and + * evt.getNewValue() is a valid Case. + */ + if ((null == evt.getOldValue()) && (evt.getNewValue() instanceof Case)) { + Case curCase = (Case) evt.getNewValue(); + + try { + // only add default evidence tag if case is open and it doesn't already exist in the tags list. + if (Case.isCaseOpen() + && Case.getCurrentCase().getServices().getTagsManager().getAllTagNames().stream() + .map(tag -> tag.getDisplayName()) + .filter(tagName -> Bundle.caseeventlistener_evidencetag().equals(tagName)) + .collect(Collectors.toList()) + .isEmpty()) { + curCase.getServices().getTagsManager().addTagName(Bundle.caseeventlistener_evidencetag()); + } + } catch (TagsManager.TagNameAlreadyExistsException ex) { + LOGGER.info("Evidence tag already exists"); // NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Error adding tag.", ex); // NON-NLS + } + + EamCase curCeCase = new EamCase( + -1, + curCase.getName(), // unique case ID + EamOrganization.getDefault(), + curCase.getDisplayName(), + curCase.getCreatedDate(), + curCase.getNumber(), + curCase.getExaminer(), + "", + "", + ""); + + if (!dbManager.isEnabled()) { + break; + } + + try { + // NOTE: Cannot determine if the opened case is a new case or a reopened case, + // so check for existing name in DB and insert if missing. + EamCase existingCase = dbManager.getCaseDetails(curCeCase.getCaseUUID()); + + if (null == existingCase) { + dbManager.newCase(curCeCase); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to enterprise artifact manager database.", ex); //NON-NLS + } + } + } // CURRENT_CASE + break; + } + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/IngestEventsListener.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/IngestEventsListener.java new file mode 100644 index 0000000000..820c23c4f9 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/IngestEventsListener.java @@ -0,0 +1,184 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.eventlisteners; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import static java.lang.Boolean.FALSE; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Listen for ingest events and update entries in the enterprise artifact manager database accordingly + */ +public class IngestEventsListener { + + private static final Logger LOGGER = Logger.getLogger(EamArtifact.class.getName()); + + final Collection addedCeArtifactTrackerSet = new LinkedHashSet<>(); + + private final PropertyChangeListener pcl1 = new IngestModuleEventListener(); + private final PropertyChangeListener pcl2 = new IngestJobEventListener(); + + /* + * Add all of our Ingest Event Listeners to the IngestManager Instance. + */ + public void installListeners() { + IngestManager.getInstance().addIngestModuleEventListener(pcl1); + IngestManager.getInstance().addIngestJobEventListener(pcl2); + } + + /* + * Remove all of our Ingest Event Listeners from the IngestManager Instance. + */ + public void uninstallListeners() { + IngestManager.getInstance().removeIngestModuleEventListener(pcl1); + IngestManager.getInstance().removeIngestJobEventListener(pcl2); + } + + private class IngestModuleEventListener implements PropertyChangeListener { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + EamDb dbManager = EamDb.getInstance(); + switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) { + case DATA_ADDED: { + if (!dbManager.isEnabled()) { + LOGGER.log(Level.WARNING, "Error connecting to database, enteprise artifact manager artifacts not created"); + return; + } + + final ModuleDataEvent mde = (ModuleDataEvent) evt.getOldValue(); + Collection bbArtifacts = mde.getArtifacts(); + if (null == bbArtifacts) { + LOGGER.log(Level.WARNING, "Error getting artifacts from Module Data Event. getArtifacts() returned null."); + return; + } + List eamArtifacts = new ArrayList<>(); + try { + for (BlackboardArtifact bbArtifact : bbArtifacts) { + // eamArtifact will be null OR a EamArtifact containing one EnterpriseArtifactManagerArtifactInstance. + EamArtifact eamArtifact = EamArtifactUtil.fromBlackboardArtifact(bbArtifact, true, dbManager.getCorrelationArtifactTypes(), true); + if (null != eamArtifact) { + + try { + // Only do something with this artifact if it's unique within the job + if (addedCeArtifactTrackerSet.add(eamArtifact.toString())) { + // Was it previously marked as bad? + // query db for artifact instances having this TYPE/VALUE and knownStatus = "Bad". + // if gettKnownStatus() is "Unknown" and this artifact instance was marked bad in a previous case, + // create TSK_INTERESTING_ARTIFACT_HIT artifact on BB. + List caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact); + if (!caseDisplayNames.isEmpty()) { + postCorrelatedBadArtifactToBlackboard(bbArtifact, + caseDisplayNames); + } + eamArtifacts.add(eamArtifact); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error counting known bad artifacts.", ex); + } + } + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting enterprise artifact manager artifact types.", ex); + } + if (FALSE == eamArtifacts.isEmpty()) { + // send update to entperirse artifact manager db + Runnable r = new NewArtifactsRunner(eamArtifacts); + // TODO: send r into a thread pool instead + Thread t = new Thread(r); + t.start(); + } // DATA_ADDED + break; + } + } + } + } + + private class IngestJobEventListener implements PropertyChangeListener { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + switch (IngestManager.IngestJobEvent.valueOf(evt.getPropertyName())) { + case DATA_SOURCE_ANALYSIS_COMPLETED: { + // clear the tracker to reduce memory usage + // @@@ This isnt' entirely accurate to do here. We could have multiple + // ingest jobs at the same time + addedCeArtifactTrackerSet.clear(); + + } // DATA_SOURCE_ANALYSIS_COMPLETED + break; + } + } + } + + @NbBundle.Messages({"enterpriseartifactmanager.prevcases.text=Previous Cases", + "enterpriseartifactmanager.ingestmodule.name=Enterprise Artifact Manager"}) + private void postCorrelatedBadArtifactToBlackboard(BlackboardArtifact bbArtifact, List caseDisplayNames) { + + try { + AbstractFile af = bbArtifact.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); + + String MODULE_NAME = Bundle.enterpriseartifactmanager_ingestmodule_name(); + BlackboardArtifact tifArtifact = af.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT); + BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, + Bundle.enterpriseartifactmanager_prevcases_text()); + BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, + "Previous Case: " + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", ""))); + tifArtifact.addAttribute(att); + tifArtifact.addAttribute(att2); + tifArtifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, bbArtifact.getArtifactID())); + + try { + // index the artifact for keyword search + Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); + blackboard.indexArtifact(tifArtifact); + } catch (Blackboard.BlackboardException ex) { + LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS + } + + // fire event to notify UI of this new artifact + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS + } catch (IllegalStateException ex) { + LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS + } + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/Installer.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/Installer.java new file mode 100644 index 0000000000..f65e5747ce --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/Installer.java @@ -0,0 +1,67 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.eventlisteners; + +import java.beans.PropertyChangeListener; +import org.openide.modules.ModuleInstall; +import org.openide.util.actions.CallableSystemAction; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.actions.EamEditCaseInfoAction; + +/** + * Install event listeners during module initialization + */ +public class Installer extends ModuleInstall { + + private static final Logger LOGGER = Logger.getLogger(Installer.class.getName()); + private static final long serialVersionUID = 1L; + private final PropertyChangeListener pcl = new CaseEventListener(); + private final IngestEventsListener ieListener = new IngestEventsListener(); + + @Override + public void restored() { + // TODO: Setup the default config settings here + // something like EnterpriseArtifactManagerSettings.setDefaults(); + + Case.addPropertyChangeListener(pcl); + ieListener.installListeners(); + + CallableSystemAction.get(EamEditCaseInfoAction.class).setEnabled(true); + + // TODO: create a thread pool to process Runners. + } + + @Override + public boolean closing() { + //platform about to close + + return true; + } + + @Override + public void uninstalled() { + //module is being unloaded + + Case.removePropertyChangeListener(pcl); + ieListener.uninstallListeners(); + + // TODO: remove thread pool + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/NewArtifactsRunner.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/NewArtifactsRunner.java new file mode 100644 index 0000000000..69f9e916c5 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/eventlisteners/NewArtifactsRunner.java @@ -0,0 +1,63 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.eventlisteners; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Thread to insert a new artifact into remote DB. + */ +public class NewArtifactsRunner implements Runnable { + + private static final Logger LOGGER = Logger.getLogger(NewArtifactsRunner.class.getName()); + private static final long serialVersionUID = 1L; + + private final EamDb dbManager; + private final Collection eamArtifacts; + + @SuppressWarnings(value = {"unchecked", "rawtypes"}) + public NewArtifactsRunner(Collection eamArtifacts) { + this.dbManager = EamDb.getInstance(); + this.eamArtifacts = new ArrayList(eamArtifacts); + } + + @Override + public void run() { + if (!dbManager.isEnabled()) { + LOGGER.log(Level.WARNING, "Enterprise artifact manager database not configured"); // NON-NLS + return; + } + + try { + for (EamArtifact eamArtifact : eamArtifacts) { + dbManager.addArtifact(eamArtifact); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error connecting to enterprise artifact manager database.", ex); //NON-NLS + } + + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/ingestmodule/IngestModule.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/ingestmodule/IngestModule.java new file mode 100644 index 0000000000..7e9a90386f --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/ingestmodule/IngestModule.java @@ -0,0 +1,342 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.ingestmodule; + +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamCase; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; +import org.sleuthkit.autopsy.ingest.IngestServices; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactInstance; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDataSource; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.HashUtility; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamOrganization; +import org.sleuthkit.datamodel.TskDataException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Ingest module for inserting entries into the enterprise artifact manager + * database on ingest of a data source + */ +@Messages({"enterpriseartifactmanager.prevcases.text=Previous Cases", + "enterpriseartifactmanager.ingestmodule.name=Enterprise Artifact Manager"}) +class IngestModule implements FileIngestModule { + + private final static Logger LOGGER = Logger.getLogger(IngestModule.class.getName()); + private final IngestServices services = IngestServices.getInstance(); + private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); + private long jobId; + private EamCase eamCase; + private EamDataSource eamDataSource; + private Blackboard blackboard; + private EamArtifact.Type filesType; + + @Override + public ProcessResult process(AbstractFile af) { + blackboard = Case.getCurrentCase().getServices().getBlackboard(); + + if ((af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) + || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS) + || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) + || (af.getKnown() == TskData.FileKnown.KNOWN) + || (af.isDir() == true)) { + return ProcessResult.OK; + } + + EamDb dbManager = EamDb.getInstance(); + + if (!dbManager.isEnabled()) { + LOGGER.log(Level.SEVERE, "Enterprise artifact manager not enabled."); // NON-NLS + return ProcessResult.ERROR; + } + + // only continue if we are correlating filesType + if (!filesType.isEnabled()) { + return ProcessResult.OK; + } + + // get the hash because we're going to correlate it + String md5 = af.getMd5Hash(); + if ((md5 == null) || (HashUtility.isNoDataMd5(md5))) { + return ProcessResult.OK; + } + + EamArtifact eamArtifact = new EamArtifact(filesType, md5); + + // If unknown to both the hash module and as a globally known artifact in the CDB, correlate to other cases + if (af.getKnown() == TskData.FileKnown.UNKNOWN) { + // query db for artifact instances having this MD5 and knownStatus = "Bad". + try { + // if af.getKnown() is "UNKNOWN" and this artifact instance was marked bad in a previous case, + // create TSK_INTERESTING_FILE artifact on BB. + List caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact); + if (!caseDisplayNames.isEmpty()) { + postCorrelatedBadFileToBlackboard(af, caseDisplayNames); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error counting known bad artifacts.", ex); // NON-NLS + return ProcessResult.ERROR; + } + } + + // Make a TSK_HASHSET_HIT blackboard artifact for global known bad files + try { + if (dbManager.isArtifactGlobalKnownBad(eamArtifact)) { + postCorrelatedHashHitToBlackboard(af); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error retrieving global known status.", ex); // NON-NLS + return ProcessResult.ERROR; + } + + try { + // TODO: add the actual device once it is implemented in Autopsy. + EamArtifactInstance cefi = new EamArtifactInstance( + eamCase, + eamDataSource, + af.getParentPath() + af.getName(), + "", + EamArtifactInstance.KnownStatus.UNKNOWN, + EamArtifactInstance.GlobalStatus.LOCAL + ); + eamArtifact.addInstance(cefi); + dbManager.prepareBulkArtifact(eamArtifact); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS + return ProcessResult.ERROR; + } + + return ProcessResult.OK; + } + + @Override + public void shutDown() { + EamDb dbManager = EamDb.getInstance(); + try { + dbManager.bulkInsertArtifacts(); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS + } + try { + Long count = dbManager.getCountArtifactInstancesByCaseDataSource(new EamArtifactInstance(eamCase, eamDataSource)); + LOGGER.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS + } + + // TODO: once we implement shared cache, if refCounter is 1, then submit data in bulk. + refCounter.decrementAndGet(jobId); + } + + // see ArtifactManagerTimeTester for details + @Messages({ + "enterpriseartifactmanager.ingestmodule.isNotEnabled=Enterprise artifact manager database settings were not properly initialized, cannot run enteprise artifact manager ingest." + }) + @Override + public void startUp(IngestJobContext context) throws IngestModuleException { + jobId = context.getJobId(); + eamCase = new EamCase(Case.getCurrentCase().getName(), Case.getCurrentCase().getDisplayName()); + + String deviceId = ""; + try { + deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(context.getDataSource().getId()).getDeviceId(); + } catch (TskCoreException | TskDataException ex) { + } + + eamDataSource = new EamDataSource(deviceId, context.getDataSource().getName()); + + EamDb dbManager = EamDb.getInstance(); + if (!dbManager.isEnabled()) { + throw new IngestModuleException(Bundle.enterpriseartifactmanager_ingestmodule_isNotEnabled()); + } + + try { + filesType = dbManager.getCorrelationArtifactTypeByName("FILES"); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error getting correlation artifact type FILES in startUp.", ex); // NON-NLS + throw new IngestModuleException("Error getting correlation artifact type FILES in startUp.", ex); // NON-NLS + } + + // TODO: once we implement a shared cache, load/init it here w/ syncronized and define reference counter + // if we are the first thread / module for this job, then make sure the case + // and image exist in the DB before we associate artifacts with it. + if (refCounter.incrementAndGet(jobId) + == 1) { + // ensure we have this data source in the CDB + try { + if (null == dbManager.getDataSourceDetails(eamDataSource.getDeviceID())) { + dbManager.newDataSource(eamDataSource); + } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error creating new data source in startUp.", ex); // NON-NLS + throw new IngestModuleException("Error creating new data source in startUp.", ex); // NON-NLS + } + + // ensure we have this case defined in the CDB + EamCase existingCase; + Case curCase = Case.getCurrentCase(); + EamCase curCeCase = new EamCase( + -1, + curCase.getName(), // unique case ID + EamOrganization.getDefault(), + curCase.getDisplayName(), + curCase.getCreatedDate(), + curCase.getNumber(), + curCase.getExaminer(), + "", + "", + ""); + try { + existingCase = dbManager.getCaseDetails(curCeCase.getCaseUUID()); + if (existingCase == null) { + dbManager.newCase(curCeCase); + } + + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Error creating new case in startUp.", ex); // NON-NLS + throw new IngestModuleException("Error creating new case in startUp.", ex); // NON-NLS + } + } + } + + private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List caseDisplayNames) { + + try { + String MODULE_NAME = Bundle.enterpriseartifactmanager_ingestmodule_name(); + BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); + BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, + Bundle.enterpriseartifactmanager_prevcases_text()); + BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, + "Previous Case: " + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", ""))); + tifArtifact.addAttribute(att); + tifArtifact.addAttribute(att2); + + try { + // index the artifact for keyword search + blackboard.indexArtifact(tifArtifact); + } catch (Blackboard.BlackboardException ex) { + LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS + } + + // send inbox message + sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash()); + + // fire event to notify UI of this new artifact + services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS + } catch (IllegalStateException ex) { + LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS + } + } + + private void postCorrelatedHashHitToBlackboard(AbstractFile abstractFile) { + try { + String MODULE_NAME = Bundle.enterpriseartifactmanager_ingestmodule_name(); + BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT); + BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, + Bundle.enterpriseartifactmanager_prevcases_text()); + tifArtifact.addAttribute(att); + + try { + // index the artifact for keyword search + blackboard.indexArtifact(tifArtifact); + } catch (Blackboard.BlackboardException ex) { + LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS + } + + // send inbox message + sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash()); + + // fire event to notify UI of this new artifact + services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS + } catch (IllegalStateException ex) { + LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS + } + } + + /** + * Post a message to the ingest inbox alerting the user that a bad file was + * found. + * + * @param artifact badFile Blackboard Artifact + * @param name badFile's name + * @param md5Hash badFile's md5 hash + */ + @Messages({"IngestModule.postToBB.fileName=File Name", + "IngestModule.postToBB.md5Hash=MD5 Hash", + "IngestModule.postToBB.hashSetSource=Source of Hash", + "IngestModule.postToBB.eamHit=Enterprise Artifact Manager", + "# {0} - Name of file that is Known Bad", + "IngestModule.postToBB.knownBadMsg=Known Bad: {0}"}) + public void sendBadFileInboxMessage(BlackboardArtifact artifact, String name, String md5Hash) { + StringBuilder detailsSb = new StringBuilder(); + //details + detailsSb.append(""); //NON-NLS + //hit + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + detailsSb.append(""); //NON-NLS + + detailsSb.append("
") //NON-NLS + .append(Bundle.IngestModule_postToBB_fileName()) + .append("") //NON-NLS + .append(name) + .append("
") //NON-NLS + .append(Bundle.IngestModule_postToBB_md5Hash()) + .append("").append(md5Hash).append("
") //NON-NLS + .append(Bundle.IngestModule_postToBB_hashSetSource()) + .append("").append(Bundle.IngestModule_postToBB_eamHit()).append("
"); //NON-NLS + + services.postMessage(IngestMessage.createDataMessage(IngestModuleFactory.getModuleName(), + Bundle.IngestModule_postToBB_knownBadMsg(name), + detailsSb.toString(), + name + md5Hash, + artifact)); + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/ingestmodule/IngestModuleFactory.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/ingestmodule/IngestModuleFactory.java new file mode 100644 index 0000000000..42e630b736 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/ingestmodule/IngestModuleFactory.java @@ -0,0 +1,78 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.ingestmodule; + +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.ingest.FileIngestModule; +import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel.EamGlobalSettingsPanel; + +/** + * Factory for enterprise artifact manager ingest modules + */ +@ServiceProvider(service = org.sleuthkit.autopsy.ingest.IngestModuleFactory.class) +public class IngestModuleFactory extends IngestModuleFactoryAdapter { + + private static final String VERSION_NUMBER = "0.8.0"; + + static String getModuleName() { + return java.util.ResourceBundle.getBundle("org/sleuthkit/enterpriseartifactmanager/Bundle") + .getString("OpenIDE-Module-Name"); + } + + @Override + public String getModuleDisplayName() { + return getModuleName(); + } + + @Override + public String getModuleDescription() { + return ""; + } + + @Override + public String getModuleVersionNumber() { + return VERSION_NUMBER; + } + + @Override + public boolean isFileIngestModuleFactory() { + return true; + } + + @Override + public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings ingestOptions) { + return new IngestModule(); + } + + @Override + public boolean hasGlobalSettingsPanel() { + return true; + } + + @Override + public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() { + EamGlobalSettingsPanel globalOptionsPanel = new EamGlobalSettingsPanel(); + globalOptionsPanel.load(); + return globalOptionsPanel; + } + +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/license-enterpriseartifactmanager.txt b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/license-enterpriseartifactmanager.txt new file mode 100644 index 0000000000..9e42551e15 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/license-enterpriseartifactmanager.txt @@ -0,0 +1,19 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/Bundle.properties new file mode 100644 index 0000000000..888663918a --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/Bundle.properties @@ -0,0 +1,67 @@ +OptionsCategory_Name_Enterprise_Artifact_Manager_Options=EnterpriseArtifactManager +OptionsCategory_Keywords_Enterprise_Artifact_Manager_Options=EnterpriseArtifactManager Settings +EnterpriseArtifactManagerImportDatabaseDialog.known.text=Known +EnterpriseArtifactManagerImportDatabaseDialog.knownBad.text=Known Bad +EnterpriseArtifactManagerImportDatabaseDialog.importHashDbMsg=Import Hash Database +EnterpriseArtifactManagerImportDatabaseDialog.fileNameExtFilter.text=Hash Database File +EnterpriseArtifactManagerImportDatabaseDialog.failedToGetDbPathMsg=Failed to get the path of the selected database. +EnterpriseArtifactManagerImportDatabaseDialog.importHashDbErr=Import Hash Database Error +EnterpriseArtifactManagerImportDatabaseDialog.mustSelectHashDbFilePathMsg=A hash database file path must be selected. +EnterpriseArtifactManagerImportDatabaseDialog.hashDbDoesNotExistMsg=The selected hash database does not exist. +EnterpriseArtifactManagerImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg=Failed to open hash database at {0}. +EnterpriseArtifactManagerGlobalSettingsPanel.lbDatabaseSettings.text=Database Settings +EnterpriseArtifactManagerImportDatabaseDialog.tfDatasetVersion.tooltip.text=Dataset Version Number +EnterpriseArtifactManagerImportDatabaseDialog.tfDatasetName.tooltip=Name for this dataset +EnterpriseArtifactManagerAddNewOrganizationDialog.tfName.tooltip=POC Name +EnterpriseArtifactManagerGlobalSettingsPanel.correlationTypes.title=Correlation Types +EnterpriseArtifactManagerManageTagDialog.lbInstructions.text= +EnterpriseArtifactManagerManageTagDialog.lbWarningMsg.text= +EnterpriseArtifactManagerGlobalSettingsPanel.bnImportDatabase.label=Import Globally Known Artifacts +EamAddNewOrganizationDialog.bnOK.text=OK +EamAddNewOrganizationDialog.lbPocPhone.text=Phone: +EamAddNewOrganizationDialog.lbPocEmail.text=Email: +EamAddNewOrganizationDialog.lbPocName.text=Name: +EamAddNewOrganizationDialog.lbPocHeading.text=Point of Contact: +EamAddNewOrganizationDialog.lbOrganizationName.text=Organization Name: +EamAddNewOrganizationDialog.bnCancel.text=Cancel +EamGlobalSettingsPanel.bnImportDatabase.actionCommand= +EamGlobalSettingsPanel.cbEnableEnterpriseArtifactManager.text=Enable Enterprise Artifact Manager +EamGlobalSettingsPanel.bnManageTypes.text=Manage Correlation Types +EamGlobalSettingsPanel.bnManageTags.actionCommand= +EamGlobalSettingsPanel.bnManageTags.toolTipText= +EamGlobalSettingsPanel.bnManageTags.text=Manage Correlatable Tags +EamGlobalSettingsPanel.tbOops.text= +EamGlobalSettingsPanel.bnConfigureDatabaseSettings.text=Configure +EamGlobalSettingsPanel.lbDatabasePlatform.text=Enable Database Platform : +EamImportDatabaseDialog.lbDatabaseType.text=Type of database: +EamImportDatabaseDialog.knownBadRadioButton.text=Known Bad +EamImportDatabaseDialog.bnNewOrganization.text=Add New Organization +EamImportDatabaseDialog.knownRadioButton.text=Known (NSRL or other) +EamImportDatabaseDialog.openButton.text=Open... +EamImportDatabaseDialog.databasePathTextField.text= +EamImportDatabaseDialog.cancelButton.text=Cancel +EamImportDatabaseDialog.lbDatasetVersion.text=Dataset Version: +EamImportDatabaseDialog.okButton.text=OK +EamImportDatabaseDialog.lbDatasetName.text=Dataset Name: +EamImportDatabaseDialog.lbSoureamOrganization.text=Source Organization: +EamImportDatabaseDialog.lbDatasetAttribution.text=Dataset Attribution: +EamImportDatabaseDialog.lbDatabasePath.text=Database Path: +EamManageTagDialog.cancelButton.text=Cancel +EamManageTagDialog.okButton.text=OK +EamPostgresSettingsDialog.lbUserPassword.text=User Password : +EamPostgresSettingsDialog.lbUserName.text=User Name : +EamPostgresSettingsDialog.bnCancel.text=Cancel +EamPostgresSettingsDialog.lbPort.text=Port : +EamPostgresSettingsDialog.bnSave.text=Save +EamPostgresSettingsDialog.lbHostName.text=Host Name / IP : +EamPostgresSettingsDialog.bnTestConnection.text=Test Connection +EamPostgresSettingsDialog.lbDatabaseName.text=Database name : +EamSqliteSettingsDialog.bnSave.text=Save +EamSqliteSettingsDialog.bnCancel.text=Cancel +EamSqliteSettingsDialog.lbTestDatabase.text= +EamSqliteSettingsDialog.bnTestDatabase.text=Test Connection +EamSqliteSettingsDialog.lbTestDatabaseWarning.text= +EamSqliteSettingsDialog.bnDatabasePathFileOpen.text=Open... +EamSqliteSettingsDialog.tfDatabasePath.toolTipText=Filename and path to store SQLite db file +EamSqliteSettingsDialog.tfDatabasePath.text= +EamSqliteSettingsDialog.lbDatabasePath.text=Database Path : diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamAddNewOrganizationDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamAddNewOrganizationDialog.form new file mode 100644 index 0000000000..aee918e68c --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamAddNewOrganizationDialog.form @@ -0,0 +1,183 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamAddNewOrganizationDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamAddNewOrganizationDialog.java new file mode 100644 index 0000000000..deacc21816 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamAddNewOrganizationDialog.java @@ -0,0 +1,338 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.util.ArrayList; +import java.util.Collection; +import java.util.logging.Level; +import javax.swing.JFrame; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamOrganization; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Dialog to add a new organization to the enterprise artifact manager database + */ +public class EamAddNewOrganizationDialog extends javax.swing.JDialog { + + private static final Logger LOGGER = Logger.getLogger(EamAddNewOrganizationDialog.class.getName()); + + private final Collection textBoxes; + private final TextBoxChangedListener textBoxChangedListener; + private boolean hasChanged; + + /** + * Creates new form EnterpriseArtifactManagerAddNewOrganizationDialog + */ + @Messages({"EnterpriseArtifactManagerAddNewOrganizationDialog.addNewOrg.msg=Add New Organization"}) + public EamAddNewOrganizationDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), + Bundle.EnterpriseArtifactManagerAddNewOrganizationDialog_addNewOrg_msg(), + true); // NON-NLS + textBoxes = new ArrayList<>(); + textBoxChangedListener = new TextBoxChangedListener(); + hasChanged = false; + initComponents(); + customizeComponents(); + display(); + } + + private void display() { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + + private void customizeComponents() { + setTextBoxListeners(); + enableOkButton(false); + } + + /** + * Register for notifications when the text boxes get updated. + */ + private void setTextBoxListeners() { + textBoxes.add(tfOrganizationName); + textBoxes.add(tfPocEmail); + textBoxes.add(tfPocPhone); + textBoxes.add(tfPocName); + addDocumentListeners(textBoxes, textBoxChangedListener); + } + + /** + * Adds a change listener to a collection of text fields. + * + * @param textFields The text fields. + * @param listener The change listener. + */ + private static void addDocumentListeners(Collection textFields, TextBoxChangedListener listener) { + textFields.forEach((textField) -> { + textField.getDocument().addDocumentListener(listener); + }); + } + + /** + * Tests whether or not values have been entered in all of the database + * settings text fields. + * + * @return True or false. + */ + private boolean requiredFieldsArePopulated() { + return !tfOrganizationName.getText().trim().isEmpty(); + } + + /** + * Tests whether or not all of the settings components are populated. + * + * @return True or false. + */ + @Messages({"EnterpriseArtifactManagerAddNewOrganizationDialog.validation.incompleteFields=Organization Name is required."}) + private boolean checkFields() { + boolean result = true; + + boolean isPopulated = requiredFieldsArePopulated(); + + if (!isPopulated) { + // We don't even have everything filled out + result = false; + lbWarningMsg.setText(Bundle.EnterpriseArtifactManagerAddNewOrganizationDialog_validation_incompleteFields()); + } + return result; + } + + /** + * Validates that the form is filled out correctly for our usage. + * + * @return true if it's okay, false otherwise. + */ + public boolean valid() { + lbWarningMsg.setText(""); + + return enableOkButton(checkFields()); + } + + /** + * Enables the "OK" button to save the new organization. + * + * @param enable + * + * @return True or False + */ + private boolean enableOkButton(Boolean enable) { + bnOK.setEnabled(enable); + return enable; + } + + /** + * Used to listen for changes in text boxes. It lets the panel know things + * have been updated and that validation needs to happen. + */ + private class TextBoxChangedListener implements DocumentListener { + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + } + + public boolean isChanged() { + return hasChanged; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + bnOK = new javax.swing.JButton(); + bnCancel = new javax.swing.JButton(); + lbOrganizationName = new javax.swing.JLabel(); + lbPocHeading = new javax.swing.JLabel(); + lbPocName = new javax.swing.JLabel(); + lbPocEmail = new javax.swing.JLabel(); + lbPocPhone = new javax.swing.JLabel(); + tfPocName = new javax.swing.JTextField(); + tfPocEmail = new javax.swing.JTextField(); + tfPocPhone = new javax.swing.JTextField(); + tfOrganizationName = new javax.swing.JTextField(); + lbWarningMsg = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(bnOK, org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "EamAddNewOrganizationDialog.bnOK.text")); // NOI18N + bnOK.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnOKActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "EamAddNewOrganizationDialog.bnCancel.text")); // NOI18N + bnCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnCancelActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(lbOrganizationName, org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "EamAddNewOrganizationDialog.lbOrganizationName.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPocHeading, org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "EamAddNewOrganizationDialog.lbPocHeading.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPocName, org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "EamAddNewOrganizationDialog.lbPocName.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPocEmail, org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "EamAddNewOrganizationDialog.lbPocEmail.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPocPhone, org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "EamAddNewOrganizationDialog.lbPocPhone.text")); // NOI18N + + tfPocName.setToolTipText(org.openide.util.NbBundle.getMessage(EamAddNewOrganizationDialog.class, "CorrelationEngineAddNewOrganizationDialog.tfName.tooltip")); // NOI18N + + lbWarningMsg.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N + lbWarningMsg.setForeground(new java.awt.Color(255, 0, 0)); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(258, Short.MAX_VALUE) + .addComponent(bnOK) + .addGap(18, 18, 18) + .addComponent(bnCancel) + .addGap(12, 12, 12)) + .addGroup(layout.createSequentialGroup() + .addGap(39, 39, 39) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbPocEmail) + .addComponent(lbPocName) + .addComponent(lbPocPhone)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tfPocPhone) + .addComponent(tfPocName) + .addComponent(tfPocEmail)) + .addContainerGap()) + .addGroup(layout.createSequentialGroup() + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lbOrganizationName, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(tfOrganizationName)) + .addGroup(layout.createSequentialGroup() + .addComponent(lbPocHeading) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(lbWarningMsg, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbOrganizationName, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tfOrganizationName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lbPocHeading) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbPocName) + .addComponent(tfPocName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbPocEmail) + .addComponent(tfPocEmail, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbPocPhone) + .addComponent(tfPocPhone, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbWarningMsg, javax.swing.GroupLayout.DEFAULT_SIZE, 22, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnOK) + .addComponent(bnCancel)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed + dispose(); + }//GEN-LAST:event_bnCancelActionPerformed + + @Messages({"EnterpriseArtifactManagerAddNewOrganizationDialog.bnOk.addFailed.text=Failed to add new organization."}) + private void bnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnOKActionPerformed + EamOrganization newOrg = new EamOrganization( + tfOrganizationName.getText(), + tfPocName.getText(), + tfPocEmail.getText(), + tfPocPhone.getText()); + try { + EamDb dbManager = EamDb.getInstance(); + dbManager.newOrganization(newOrg); + hasChanged = true; + dispose(); + } catch (EamDbException ex) { + lbWarningMsg.setText(Bundle.EnterpriseArtifactManagerAddNewOrganizationDialog_bnOk_addFailed_text()); + LOGGER.log(Level.SEVERE, "Failed adding new organization.", ex); + } + }//GEN-LAST:event_bnOKActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnCancel; + private javax.swing.JButton bnOK; + private javax.swing.JLabel lbOrganizationName; + private javax.swing.JLabel lbPocEmail; + private javax.swing.JLabel lbPocHeading; + private javax.swing.JLabel lbPocName; + private javax.swing.JLabel lbPocPhone; + private javax.swing.JLabel lbWarningMsg; + private javax.swing.JTextField tfOrganizationName; + private javax.swing.JTextField tfPocEmail; + private javax.swing.JTextField tfPocName; + private javax.swing.JTextField tfPocPhone; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamGlobalSettingsPanel.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamGlobalSettingsPanel.form new file mode 100644 index 0000000000..f8eba16a76 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamGlobalSettingsPanel.form @@ -0,0 +1,277 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamGlobalSettingsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamGlobalSettingsPanel.java new file mode 100644 index 0000000000..d347375972 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamGlobalSettingsPanel.java @@ -0,0 +1,582 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import org.sleuthkit.autopsy.coreutils.Logger; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.JComboBox; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.corecomponents.OptionsPanel; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.events.AutopsyEvent; +import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbPlatformEnum; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Main settings panel for the enterprise artifact manager + */ +public final class EamGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(EamGlobalSettingsPanel.class.getName()); + + private final IngestJobEventPropertyChangeListener ingestJobEventListener; + + private boolean dbConfigured; + private boolean initiallyEnabled; + private boolean requireReboot; + private boolean comboboxSelectDatabaseTypeActionListenerActive; + + /** + * Creates new form EnterpriseArtifactManagerOptionsPanel + */ + public EamGlobalSettingsPanel() { + ingestJobEventListener = new IngestJobEventPropertyChangeListener(); + + initComponents(); + customizeComponents(); + addIngestJobEventsListener(); + } + + @Messages({"EnterpriseArtifactManagerGlobalSettingsPanel.title=Global Enterprise Artifact Manager Settings"}) + private void customizeComponents() { + setName(Bundle.EnterpriseArtifactManagerGlobalSettingsPanel_title()); + comboboxSelectDatabaseTypeActionListenerActive = false; // don't fire action listener while loading combobox content + comboboxSelectDatabaseType.removeAllItems(); + for (EamDbPlatformEnum p : EamDbPlatformEnum.values()) { + comboboxSelectDatabaseType.addItem(p.toString()); + } + comboboxSelectDatabaseTypeActionListenerActive = true; + } + + private void addIngestJobEventsListener() { + IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener); + ingestStateUpdated(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane = new javax.swing.JScrollPane(); + pnOverallPanel = new javax.swing.JPanel(); + pnSettings = new javax.swing.JPanel(); + bnImportDatabase = new javax.swing.JButton(); + pnDatabaseConnectionSettings = new javax.swing.JPanel(); + comboboxSelectDatabaseType = new javax.swing.JComboBox<>(); + lbDatabasePlatform = new javax.swing.JLabel(); + bnConfigureDatabaseSettings = new javax.swing.JButton(); + tbOops = new javax.swing.JTextField(); + bnManageTags = new javax.swing.JButton(); + bnManageTypes = new javax.swing.JButton(); + cbEnableCorrelationEngine = new javax.swing.JCheckBox(); + + setName(""); // NOI18N + + jScrollPane.setBorder(null); + + bnImportDatabase.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/import16.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(bnImportDatabase, org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "CorrelationEngineGlobalSettingsPanel.bnImportDatabase.label")); // NOI18N + bnImportDatabase.setActionCommand(org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.bnImportDatabase.actionCommand")); // NOI18N + bnImportDatabase.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnImportDatabaseActionPerformed(evt); + } + }); + + pnDatabaseConnectionSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "CorrelationEngineGlobalSettingsPanel.lbDatabaseSettings.text"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N + pnDatabaseConnectionSettings.setName(""); // NOI18N + + comboboxSelectDatabaseType.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "test 1", "test 2" })); + comboboxSelectDatabaseType.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + comboboxSelectDatabaseTypeActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(lbDatabasePlatform, org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.lbDatabasePlatform.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnConfigureDatabaseSettings, org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.bnConfigureDatabaseSettings.text")); // NOI18N + bnConfigureDatabaseSettings.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnConfigureDatabaseSettingsActionPerformed(evt); + } + }); + + javax.swing.GroupLayout pnDatabaseConnectionSettingsLayout = new javax.swing.GroupLayout(pnDatabaseConnectionSettings); + pnDatabaseConnectionSettings.setLayout(pnDatabaseConnectionSettingsLayout); + pnDatabaseConnectionSettingsLayout.setHorizontalGroup( + pnDatabaseConnectionSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnDatabaseConnectionSettingsLayout.createSequentialGroup() + .addContainerGap() + .addComponent(lbDatabasePlatform) + .addGap(18, 18, 18) + .addComponent(comboboxSelectDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(bnConfigureDatabaseSettings) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + pnDatabaseConnectionSettingsLayout.setVerticalGroup( + pnDatabaseConnectionSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnDatabaseConnectionSettingsLayout.createSequentialGroup() + .addGap(3, 3, 3) + .addGroup(pnDatabaseConnectionSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbDatabasePlatform, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(comboboxSelectDatabaseType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(bnConfigureDatabaseSettings))) + ); + + tbOops.setEditable(false); + tbOops.setFont(tbOops.getFont().deriveFont(tbOops.getFont().getStyle() | java.awt.Font.BOLD, 12)); + tbOops.setForeground(new java.awt.Color(255, 0, 0)); + tbOops.setText(org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.tbOops.text")); // NOI18N + tbOops.setBorder(null); + + org.openide.awt.Mnemonics.setLocalizedText(bnManageTags, org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.bnManageTags.text")); // NOI18N + bnManageTags.setToolTipText(org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.bnManageTags.toolTipText")); // NOI18N + bnManageTags.setActionCommand(org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.bnManageTags.actionCommand")); // NOI18N + bnManageTags.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnManageTagsActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(bnManageTypes, org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.bnManageTypes.text")); // NOI18N + bnManageTypes.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnManageTypesActionPerformed(evt); + } + }); + + javax.swing.GroupLayout pnSettingsLayout = new javax.swing.GroupLayout(pnSettings); + pnSettings.setLayout(pnSettingsLayout); + pnSettingsLayout.setHorizontalGroup( + pnSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnSettingsLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(tbOops) + .addGroup(pnSettingsLayout.createSequentialGroup() + .addGroup(pnSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pnDatabaseConnectionSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(pnSettingsLayout.createSequentialGroup() + .addComponent(bnImportDatabase) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(bnManageTags) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(bnManageTypes))) + .addGap(0, 188, Short.MAX_VALUE))) + .addContainerGap()) + ); + pnSettingsLayout.setVerticalGroup( + pnSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnSettingsLayout.createSequentialGroup() + .addGap(8, 8, 8) + .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pnDatabaseConnectionSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 194, Short.MAX_VALUE) + .addGroup(pnSettingsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnImportDatabase) + .addComponent(bnManageTags, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(bnManageTypes, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(34, 34, 34)) + ); + + cbEnableCorrelationEngine.setFont(cbEnableCorrelationEngine.getFont().deriveFont(cbEnableCorrelationEngine.getFont().getStyle() & ~java.awt.Font.BOLD, 11)); + org.openide.awt.Mnemonics.setLocalizedText(cbEnableCorrelationEngine, org.openide.util.NbBundle.getMessage(EamGlobalSettingsPanel.class, "EamGlobalSettingsPanel.cbEnableCorrelationEngine.text")); // NOI18N + cbEnableCorrelationEngine.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + cbEnableCorrelationEngineItemStateChanged(evt); + } + }); + + javax.swing.GroupLayout pnOverallPanelLayout = new javax.swing.GroupLayout(pnOverallPanel); + pnOverallPanel.setLayout(pnOverallPanelLayout); + pnOverallPanelLayout.setHorizontalGroup( + pnOverallPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnOverallPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnOverallPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnOverallPanelLayout.createSequentialGroup() + .addComponent(cbEnableCorrelationEngine, javax.swing.GroupLayout.PREFERRED_SIZE, 215, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(pnSettings, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + ); + pnOverallPanelLayout.setVerticalGroup( + pnOverallPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnOverallPanelLayout.createSequentialGroup() + .addComponent(cbEnableCorrelationEngine) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pnSettings, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + jScrollPane.setViewportView(pnOverallPanel); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jScrollPane) + .addGap(2, 2, 2)) + ); + }// //GEN-END:initComponents + + private void cbEnableEnterpriseArtifactManagerItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_cbEnableEnterpriseArtifactManagerItemStateChanged + tbOops.setText(""); + if (!cbEnableEnterpriseArtifactManager.isSelected()) { + enableAllSubComponents(false); + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } else { + enableDatabaseSubComponents(true); + validateDatabaseSettings(); + } + }//GEN-LAST:event_cbEnableEnterpriseArtifactManagerItemStateChanged + + private void bnImportDatabaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnImportDatabaseActionPerformed + EamImportDatabaseDialog dialog = new EamImportDatabaseDialog(); + firePropertyChange(OptionsPanelController.PROP_VALID, null, null); + }//GEN-LAST:event_bnImportDatabaseActionPerformed + + /** + * When the "Configure" button is clicked, open the proper dialog. + * + * @param evt Button event + */ + @Messages({"EnterpriseArtifactManagerGlobalSettingsPanel.configureButton.errorLabel=You must select a valid platform in the drop down box.", + "EnterpriseArtifactManagerGlobalSettingsPanel.configureButton.errorTitle=Invalid platform selection."}) + private void bnConfigureDatabaseSettingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnConfigureDatabaseSettingsActionPerformed + EamDbPlatformEnum selectedPlatform = EamDbPlatformEnum.getSelectedPlatform(); + Boolean dbConfigChanged = false; + + switch (selectedPlatform) { + case SQLITE: + EamSqliteSettingsDialog dialogS = new EamSqliteSettingsDialog(); + dbConfigChanged = dialogS.isChanged(); + break; + + case POSTGRESQL: + EamPostgresSettingsDialog dialogP = new EamPostgresSettingsDialog(); + dbConfigChanged = dialogP.isChanged(); + break; + + default: + JOptionPane.showMessageDialog(null, Bundle.EnterpriseArtifactManagerGlobalSettingsPanel_configureButton_errorLabel(), + Bundle.EnterpriseArtifactManagerGlobalSettingsPanel_configureButton_errorTitle(), + JOptionPane.ERROR_MESSAGE); + break; + } + + if (dbConfigChanged) { + if (initiallyEnabled || dbConfigured) { + requireReboot = true; + enableButtonSubComponents(false); + } + dbConfigured = true; + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + } + }//GEN-LAST:event_bnConfigureDatabaseSettingsActionPerformed + + /** + * When there is a change to the combobox, update the selectedPlatform. + * + * @param evt + */ + @SuppressWarnings({"unchecked cast", "unchecked"}) + private void comboboxSelectDatabaseTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboboxSelectDatabaseTypeActionPerformed + if (comboboxSelectDatabaseTypeActionListenerActive) { + JComboBox cb = (JComboBox) evt.getSource(); + String platformName = (String) cb.getSelectedItem(); + EamDbPlatformEnum.setSelectedPlatform(platformName); + } + }//GEN-LAST:event_comboboxSelectDatabaseTypeActionPerformed + + private void bnManageTagsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnManageTagsActionPerformed + EamManageTagDialog dialog = new EamManageTagDialog(); + firePropertyChange(OptionsPanelController.PROP_VALID, null, null); + }//GEN-LAST:event_bnManageTagsActionPerformed + + private void bnManageTypesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnManageTypesActionPerformed + EamTypesSelectionDialog dialogT = new EamTypesSelectionDialog(); + firePropertyChange(OptionsPanelController.PROP_VALID, null, null); + }//GEN-LAST:event_bnManageTypesActionPerformed + + @Override + public void load() { + tbOops.setText(""); + + enableAllSubComponents(false); + + initiallyEnabled = Boolean.valueOf(ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.enabled")); // NON-NLS + cbEnableEnterpriseArtifactManager.setSelected(initiallyEnabled); // NON-NLS + String selectedPlatformString = ModuleSettings.getConfigSetting("EnterpriseArtifactManager", "db.selectedPlatform"); // NON-NLS + dbConfigured = selectedPlatformString != null; + requireReboot = false; + + if (dbConfigured) { + comboboxSelectDatabaseTypeActionListenerActive = false; // don't fire action listener while configuring combobox content + comboboxSelectDatabaseType.setSelectedIndex(EamDbPlatformEnum.getSelectedPlatform().ordinal()); + comboboxSelectDatabaseTypeActionListenerActive = true; // don't fire action listener while loading combobox content + } + if (this.valid() && initiallyEnabled) { + enableButtonSubComponents(true); + } + this.ingestStateUpdated(); + } + + @Override + public void store() { // Click OK or Apply on Options Panel + saveSettings(); + } + + /** + * Validates that the form is filled out correctly for our usage. + * + * @return true if it's okay, false otherwise. + */ + boolean valid() { + tbOops.setText(""); + + if (cbEnableEnterpriseArtifactManager.isSelected()) { + return validateDatabaseSettings(); + } else { + return true; + } + } + + /** + * Validate the Database Settings panel + * + * @return true or false + */ + @Messages({"EnterpriseArtifactManagerGlobalSettingsPanel.validate.mustConfigureDb.text=You must configure the database."}) + private boolean validateDatabaseSettings() { + if (!dbConfigured) { + tbOops.setText(Bundle.EnterpriseArtifactManagerGlobalSettingsPanel_validate_mustConfigureDb_text()); + return false; + } + + return true; + } + + @Messages({"EnterpriseArtifactManagerGlobalSettingsPanel.restartRequiredTitle.text=Application restart required", + "EnterpriseArtifactManagerGlobalSettingsPanel.restartRequiredLabel.text=Autopsy must be restarted for new configuration to take effect."}) + @Override + public void saveSettings() { // Click OK on Global Settings Panel + ModuleSettings.setConfigSetting("EnterpriseArtifactManager", "db.enabled", Boolean.toString(cbEnableEnterpriseArtifactManager.isSelected())); // NON-NLS + if (cbEnableEnterpriseArtifactManager.isSelected()) { + EamDbPlatformEnum.saveSelectedPlatform(); + + if (requireReboot) { + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, + Bundle.EnterpriseArtifactManagerGlobalSettingsPanel_restartRequiredLabel_text(), + Bundle.EnterpriseArtifactManagerGlobalSettingsPanel_restartRequiredTitle_text(), + JOptionPane.WARNING_MESSAGE); + }); + } else { + EamDb dbManager = EamDb.getInstance(); + dbManager.updateSettings(); + enableButtonSubComponents(true); + } + } + } + + @Override + @SuppressWarnings("FinalizeDeclaration") + protected void finalize() throws Throwable { + IngestManager.getInstance().removeIngestJobEventListener(ingestJobEventListener); + super.finalize(); + } + + /** + * An ingest job event listener that disables the options panel while an + * ingest job is running. + */ + private class IngestJobEventPropertyChangeListener implements PropertyChangeListener { + + /** + * Listens for local ingest job started, completed or cancel events and + * enables/disables the options panel according to the job state. + * + * @param event + */ + @Override + public void propertyChange(PropertyChangeEvent event) { + if (AutopsyEvent.SourceType.LOCAL == ((AutopsyEvent) event).getSourceType()) { + ingestStateUpdated(); + } + } + }; + + @Messages({"EnterpriseArtifactManagerGlobalSettingsPanel.validationErrMsg.ingestRunning=Cannot change settings while ingest is running."}) + private void ingestStateUpdated() { + if (!SwingUtilities.isEventDispatchThread()) { + SwingUtilities.invokeLater(() -> { + ingestStateUpdated(); + }); + + return; + } + + if (IngestManager.getInstance().isIngestRunning()) { + cbEnableEnterpriseArtifactManager.setEnabled(false); + tbOops.setText(Bundle.EnterpriseArtifactManagerGlobalSettingsPanel_validationErrMsg_ingestRunning()); + enableAllSubComponents(false); + } else { + cbEnableEnterpriseArtifactManager.setEnabled(true); + tbOops.setText(""); + enableAllSubComponents(cbEnableEnterpriseArtifactManager.isSelected()); + } + } + + /** + * Wrapper around each of the enableComponentXYZ methods to enable/disable + * them all at the same time. + * + * @param enable + * + * @return True + */ + private boolean enableAllSubComponents(Boolean enable) { + enableDatabaseSubComponents(enable); + enableButtonSubComponents(enable); + return true; + } + + /** + * Wrapper around each of the enableXYZ methods that configure the database + * to enable/disable them all at the same time. + * + * @param enable + * + * @return True + */ + private boolean enableDatabaseSubComponents(Boolean enable) { + enableDatabasePlatformComboBox(enable); + enableConfigureDatabasePlatformButton(enable); + return true; + } + + /** + * Wrapper around each of the enableComponentXYZButton methods to + * enable/disable them all at the same time. + * + * @param enable + * + * @return True + */ + private boolean enableButtonSubComponents(Boolean enable) { + enableManageCorrelationTypesButton(enable); + enableImportGloballyKnownArtifactsButton(enable); + enableManageTagsButton(enable); + return true; + } + + /** + * Enables the ComboBox used to select the database platform. + * + * @param enable + * + * @return True or False + */ + private boolean enableDatabasePlatformComboBox(Boolean enable) { + comboboxSelectDatabaseType.setEnabled(enable); + return enable; + } + + /** + * Enables the "Configure" button used to configure the database platform. + * + * @param enable + * + * @return True or False + */ + private boolean enableConfigureDatabasePlatformButton(Boolean enable) { + bnConfigureDatabaseSettings.setEnabled(enable); + return enable; + } + + /** + * Enables the "Import Globally Known Artifacts" button. + * + * @param enable + * + * @return True or False + */ + private boolean enableImportGloballyKnownArtifactsButton(Boolean enable) { + bnImportDatabase.setEnabled(enable); + return enable; + } + + /** + * Enables the "Manage Correlation Types" button. + * + * @param enable + * + * @return True or False + */ + private boolean enableManageCorrelationTypesButton(Boolean enable) { + bnManageTypes.setEnabled(enable); + return enable; + } + + /** + * Enables the "Manage Tags" button. + * + * @param enable + * + * @return True or False + */ + private boolean enableManageTagsButton(Boolean enable) { + bnManageTags.setEnabled(enable); + return enable; + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnConfigureDatabaseSettings; + private javax.swing.JButton bnImportDatabase; + private javax.swing.JButton bnManageTags; + private javax.swing.JButton bnManageTypes; + private javax.swing.JCheckBox cbEnableCorrelationEngine; + private javax.swing.JComboBox comboboxSelectDatabaseType; + private javax.swing.JScrollPane jScrollPane; + private javax.swing.JLabel lbDatabasePlatform; + private javax.swing.JPanel pnDatabaseConnectionSettings; + private javax.swing.JPanel pnOverallPanel; + private javax.swing.JPanel pnSettings; + private javax.swing.JTextField tbOops; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamImportDatabaseDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamImportDatabaseDialog.form new file mode 100644 index 0000000000..2ff8ad03ca --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamImportDatabaseDialog.form @@ -0,0 +1,297 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamImportDatabaseDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamImportDatabaseDialog.java new file mode 100644 index 0000000000..653134e593 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamImportDatabaseDialog.java @@ -0,0 +1,636 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 - 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.swing.SwingWorker; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifactInstance; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamGlobalFileInstance; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamGlobalSet; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamOrganization; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Instances of this class allow a user to select an existing hash database and + * add it to the set of hash databases used to classify files as unknown, known, + * or known bad. + */ +final class EamImportDatabaseDialog extends javax.swing.JDialog { + private static final Logger LOGGER = Logger.getLogger(EamImportDatabaseDialog.class.getName()); + + private final JFileChooser fileChooser = new JFileChooser(); + private final static String LAST_FILE_PATH_KEY = "EnterpriseArtifactManagerImport_Path"; // NON-NLS + private EamOrganization selectedOrg = null; + private List orgs = null; + private final Collection textBoxes; + private final TextBoxChangedListener textBoxChangedListener; + + + /** + * Displays a dialog that allows a user to select an existing hash database + * and add it to the set of hash databases used to classify files as + * unknown, known, or known bad. + */ + EamImportDatabaseDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(EamImportDatabaseDialog.class, "EnterpriseArtifactManagerImportDatabaseDialog.importHashDbMsg"), + true); // NON-NLS + textBoxes = new ArrayList<>(); + textBoxChangedListener = new TextBoxChangedListener(); + initFileChooser(); + initComponents(); + customizeComponents(); + display(); + } + + private void initFileChooser() { + fileChooser.setDragEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + String[] EXTENSION = new String[]{"idx"}; //NON-NLS + FileNameExtensionFilter filter = new FileNameExtensionFilter( + NbBundle.getMessage(this.getClass(), "EnterpriseArtifactManagerImportDatabaseDialog.fileNameExtFilter.text"), EXTENSION); // NON-NLS + fileChooser.setFileFilter(filter); + fileChooser.setMultiSelectionEnabled(false); + } + + private void customizeComponents() { + populateCombobox(); + setTextBoxListeners(); + enableOkButton(false); + } + + /** + * Register for notifications when the text boxes get updated. + */ + private void setTextBoxListeners() { + textBoxes.add(tfDatasetName); + textBoxes.add(tfDatasetVersion); + addDocumentListeners(textBoxes, textBoxChangedListener); + } + + private void populateCombobox() { + comboboxSoureamOrganization.removeAllItems(); + try { + EamDb dbManager = EamDb.getInstance(); + orgs = dbManager.getOrganizations(); + orgs.forEach((org) -> { + comboboxSoureamOrganization.addItem(org.getName()); + selectedOrg = orgs.get(0); + }); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Failure populating combobox with organizations.", ex); + } + } + + /** + * Adds a change listener to a collection of text fields. + * + * @param textFields The text fields. + * @param listener The change listener. + */ + private static void addDocumentListeners(Collection textFields, TextBoxChangedListener listener) { + textFields.forEach((textField) -> { + textField.getDocument().addDocumentListener(listener); + }); + } + + /** + * Tests whether or not values have been entered in all of the required + * text fields. + * + * @return True or false. + */ + private boolean textFieldsArePopulated() { + return !tfDatasetName.getText().trim().isEmpty() + && !tfDatasetVersion.getText().trim().isEmpty() + && !databasePathTextField.getText().trim().isEmpty(); + } + + /** + * Tests whether or not all of the settings components are populated. + * + * @return True or false. + */ + @Messages({"EnterpriseArtifactManagerImportDatabaseDialog.validation.incompleteFields=Fill in all values"}) + private boolean checkFields() { + boolean result = true; + + boolean allPopulated = textFieldsArePopulated(); + + if (!allPopulated) { + // We don't even have everything filled out + result = false; + lbWarningMsg.setText(Bundle.EnterpriseArtifactManagerImportDatabaseDialog_validation_incompleteFields()); + } + return result; + } + + /** + * Validates that the form is filled out correctly for our usage. + * + * @return true if it's okay, false otherwise. + */ + @Messages({"EnterpriseArtifactManagerImportDatabaseDialog.validation.notEnabled=Database not initialized. Restart Autopsy."}) + public boolean valid() { + lbWarningMsg.setText(""); + EamDb dbManager = EamDb.getInstance(); + if (!dbManager.isEnabled()) { + lbWarningMsg.setText(Bundle.EnterpriseArtifactManagerImportDatabaseDialog_validation_notEnabled()); + return false; + } + + return enableOkButton(checkFields()); + } + + /** + * Enables the "OK" button to create the Global File Set and insert the instances. + * + * @param enable + * + * @return True or False + */ + private boolean enableOkButton(Boolean enable) { + okButton.setEnabled(enable); + return enable; + } + + /** + * Used to listen for changes in text boxes. It lets the panel know things + * have been updated and that validation needs to happen. + */ + private class TextBoxChangedListener implements DocumentListener { + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + } + + private void display() { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + databasePathTextField = new javax.swing.JTextField(); + openButton = new javax.swing.JButton(); + knownRadioButton = new javax.swing.JRadioButton(); + knownBadRadioButton = new javax.swing.JRadioButton(); + lbDatabaseType = new javax.swing.JLabel(); + lbDatabasePath = new javax.swing.JLabel(); + lbDatasetAttribution = new javax.swing.JLabel(); + lbSourceOrganization = new javax.swing.JLabel(); + lbDatasetName = new javax.swing.JLabel(); + lbDatasetVersion = new javax.swing.JLabel(); + comboboxSourceOrganization = new javax.swing.JComboBox<>(); + tfDatasetName = new javax.swing.JTextField(); + tfDatasetVersion = new javax.swing.JTextField(); + bnNewOrganization = new javax.swing.JButton(); + lbWarningMsg = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + databasePathTextField.setText(org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.databasePathTextField.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(openButton, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.openButton.text")); // NOI18N + openButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + openButtonActionPerformed(evt); + } + }); + + buttonGroup1.add(knownRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(knownRadioButton, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.knownRadioButton.text")); // NOI18N + + buttonGroup1.add(knownBadRadioButton); + knownBadRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(knownBadRadioButton, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.knownBadRadioButton.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbDatabaseType, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.lbDatabaseType.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbDatabasePath, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.lbDatabasePath.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbDatasetAttribution, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.lbDatasetAttribution.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbSourceOrganization, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.lbSourceOrganization.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbDatasetName, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.lbDatasetName.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbDatasetVersion, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.lbDatasetVersion.text")); // NOI18N + + comboboxSourceOrganization.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + comboboxSourceOrganization.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + comboboxSourceOrganizationActionPerformed(evt); + } + }); + + tfDatasetName.setToolTipText(org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "CorrelationEngineImportDatabaseDialog.tfDatasetName.tooltip")); // NOI18N + + tfDatasetVersion.setToolTipText(org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "CorrelationEngineImportDatabaseDialog.tfDatasetVersion.tooltip.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnNewOrganization, org.openide.util.NbBundle.getMessage(EamImportDatabaseDialog.class, "EamImportDatabaseDialog.bnNewOrganization.text")); // NOI18N + bnNewOrganization.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnNewOrganizationActionPerformed(evt); + } + }); + + lbWarningMsg.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N + lbWarningMsg.setForeground(new java.awt.Color(255, 0, 0)); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lbWarningMsg, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(18, 18, 18) + .addComponent(okButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbDatabaseType) + .addGroup(layout.createSequentialGroup() + .addGap(19, 19, 19) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(knownRadioButton) + .addComponent(knownBadRadioButton))) + .addComponent(lbDatasetAttribution)) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(lbDatabasePath) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(databasePathTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 374, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addGap(23, 23, 23) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lbDatasetVersion) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(tfDatasetVersion, javax.swing.GroupLayout.PREFERRED_SIZE, 154, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(lbSourceOrganization) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(bnNewOrganization)) + .addGroup(layout.createSequentialGroup() + .addComponent(lbDatasetName) + .addGap(12, 12, 12) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(comboboxSourceOrganization, javax.swing.GroupLayout.Alignment.TRAILING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(tfDatasetName)))))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(openButton))) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, okButton}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(openButton) + .addComponent(databasePathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbDatabasePath)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbDatabaseType) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(knownRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(knownBadRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lbDatasetAttribution) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(bnNewOrganization) + .addGroup(layout.createSequentialGroup() + .addGap(3, 3, 3) + .addComponent(lbSourceOrganization, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(comboboxSourceOrganization, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(tfDatasetName) + .addComponent(lbDatasetName, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbDatasetVersion, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tfDatasetVersion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbWarningMsg, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(okButton) + .addComponent(cancelButton))) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed + if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + File databaseFile = fileChooser.getSelectedFile(); + try { + databasePathTextField.setText(databaseFile.getCanonicalPath()); + if (databaseFile.getName().toLowerCase().contains("nsrl")) { //NON-NLS + knownRadioButton.setSelected(true); + } + ModuleSettings.setConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY, databaseFile.getParent()); + } catch (IOException ex) { + Logger.getLogger(EamImportDatabaseDialog.class.getName()).log(Level.SEVERE, "Failed to get path of selected database", ex); // NON-NLS + JOptionPane.showMessageDialog(this, + NbBundle.getMessage(this.getClass(), + "EnterpriseArtifactManagerImportDatabaseDialog.failedToGetDbPathMsg")); // NON-NLS + } + } + valid(); + }//GEN-LAST:event_openButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + /** + * Create the new global set and return the ID number + * + * @return ID number of new global set + * @throws EamDbException + */ + private int createGlobalSet() throws EamDbException { + EamDb dbManager = EamDb.getInstance(); + EamGlobalSet eamGlobalSet = new EamGlobalSet( + selectedOrg.getOrgID(), + tfDatasetName.getText().trim(), + tfDatasetVersion.getText().trim(), + LocalDate.now()); + return dbManager.newGlobalSet(eamGlobalSet); + } + + @Messages({"EnterpriseArtifactManagerImportDatabaseDialog.createGlobalSet.failedMsg.text=Failed to store attribution details.", + "EnterpriseArtifactManagerImportDatabaseDialog.createGlobalSet.failedTitle.text=Import hashdb error."}) + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + // Note that the error handlers in this method call return without disposing of the + // dialog to allow the user to try again, if desired. + String selectedFilePath = databasePathTextField.getText(); + + // have valid file path + if (selectedFilePath.isEmpty()) { + JOptionPane.showMessageDialog(this, + NbBundle.getMessage(this.getClass(), + "EnterpriseArtifactManagerImportDatabaseDialog.mustSelectHashDbFilePathMsg"), + NbBundle.getMessage(this.getClass(), + "EnterpriseArtifactManagerImportDatabaseDialog.importHashDbErr"), + JOptionPane.ERROR_MESSAGE); // NON-NLS + return; + } + File file = new File(selectedFilePath); + if (!file.exists()) { + JOptionPane.showMessageDialog(this, + NbBundle.getMessage(this.getClass(), + "EnterpriseArtifactManagerImportDatabaseDialog.hashDbDoesNotExistMsg"), + NbBundle.getMessage(this.getClass(), + "EnterpriseArtifactManagerImportDatabaseDialog.importHashDbErr"), + JOptionPane.ERROR_MESSAGE); // NON-NLS + return; + } + + // create global set + int globalSetID = -1; + try { + globalSetID = createGlobalSet(); + } catch (EamDbException ex) { + JOptionPane.showMessageDialog(this, + Bundle.EnterpriseArtifactManagerImportDatabaseDialog_createGlobalSet_failedMsg_text(), + Bundle.EnterpriseArtifactManagerImportDatabaseDialog_createGlobalSet_failedTitle_text(), + JOptionPane.ERROR_MESSAGE); + return; + } + + // insert hashes + EamArtifactInstance.KnownStatus knownStatus = EamArtifactInstance.KnownStatus.UNKNOWN; + if (knownRadioButton.isSelected()) { + knownStatus = EamArtifactInstance.KnownStatus.KNOWN; + } else if (knownBadRadioButton.isSelected()) { + knownStatus = EamArtifactInstance.KnownStatus.BAD; + } + + String errorMessage = NbBundle.getMessage(this.getClass(), + "EnterpriseArtifactManagerImportDatabaseDialog.errorMessage.failedToOpenHashDbMsg", + selectedFilePath); // NON-NLS + try { + new ImportHashDatabaseWorker(selectedFilePath, knownStatus, globalSetID).execute(); + } catch (Throwable ex) { + Logger.getLogger(EamImportDatabaseDialog.class.getName()).log(Level.WARNING, errorMessage, ex); + JOptionPane.showMessageDialog(this, + ex.getMessage(), + NbBundle.getMessage(this.getClass(), + "EnterpriseArtifactManagerImportDatabaseDialog.importHashDbErr"), + JOptionPane.ERROR_MESSAGE); + return; + } + + dispose(); + }//GEN-LAST:event_okButtonActionPerformed + + @SuppressWarnings({"unchecked"}) + private void bnNewOrganizationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnNewOrganizationActionPerformed + EamAddNewOrganizationDialog dialogO = new EamAddNewOrganizationDialog(); + // update the combobox options + if (dialogO.isChanged()) { + populateCombobox(); + } + }//GEN-LAST:event_bnNewOrganizationActionPerformed + + @SuppressWarnings({"unchecked"}) + private void comboboxSoureamOrganizationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comboboxSoureamOrganizationActionPerformed + JComboBox cb = (JComboBox)evt.getSource(); + String orgName = (String)cb.getSelectedItem(); + if (null == orgName) return; + + for (EamOrganization org : orgs) { + if (org.getName().equals(orgName)) { + selectedOrg = org; + return; + } + } + }//GEN-LAST:event_comboboxSoureamOrganizationActionPerformed + + @NbBundle.Messages({"EnterpriseArtifactManagerImportDatabaseDialog.ImportHashDatabaseWorker.displayName=Importing Hash Database"}) + private class ImportHashDatabaseWorker extends SwingWorker { + + private final EamDb dbManager; + private final File file; + private final EamArtifactInstance.KnownStatus knownStatus; + private final int globalSetID; + + public ImportHashDatabaseWorker(String filename, EamArtifactInstance.KnownStatus knownStatus, int globalSetID) throws EamDbException, UnknownHostException { + this.dbManager = EamDb.getInstance(); + this.file = new File(filename); + this.knownStatus = knownStatus; + this.globalSetID = globalSetID; + + if (!dbManager.isEnabled()) { + throw new EamDbException("Enterprise artifact manager database settings were not properly initialized"); // NON-NLS + } + } + + @Override + protected Object doInBackground() throws Exception { + ProgressHandle progress = ProgressHandle.createHandle(Bundle.EnterpriseArtifactManagerImportDatabaseDialog_ImportHashDatabaseWorker_displayName()); + importHashDatabase(progress); + return null; + } + + private long numberOfLinesInFile(File f) throws IOException { + return Files.lines(f.toPath()).count(); + } + + private void importHashDatabase(ProgressHandle progress) throws EamDbException, IOException { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String line; + + long totalLines = numberOfLinesInFile(file); + if (totalLines <= Integer.MAX_VALUE) { + progress.start((int) totalLines); + } else { + progress.start(); + } + + int numLines = 0; + while ((line = reader.readLine()) != null) { + progress.progress(++numLines); + + String[] parts = line.split("\\|"); + + // Header lines start with a 41 character dummy hash, 1 character longer than a SHA-1 hash + if (parts.length != 2 || parts[0].length() == 41) { + continue; + } + + EamGlobalFileInstance eamGlobalFileInstance = new EamGlobalFileInstance( + globalSetID, + parts[0].toLowerCase(), + knownStatus, + ""); + + dbManager.prepareGlobalFileInstance(eamGlobalFileInstance); + } + + dbManager.bulkInsertGlobalFileInstances(); + progress.finish(); + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnNewOrganization; + private javax.swing.ButtonGroup buttonGroup1; + private javax.swing.JButton cancelButton; + private javax.swing.JComboBox comboboxSourceOrganization; + private javax.swing.JTextField databasePathTextField; + private javax.swing.JRadioButton knownBadRadioButton; + private javax.swing.JRadioButton knownRadioButton; + private javax.swing.JLabel lbDatabasePath; + private javax.swing.JLabel lbDatabaseType; + private javax.swing.JLabel lbDatasetAttribution; + private javax.swing.JLabel lbDatasetName; + private javax.swing.JLabel lbDatasetVersion; + private javax.swing.JLabel lbSourceOrganization; + private javax.swing.JLabel lbWarningMsg; + private javax.swing.JButton okButton; + private javax.swing.JButton openButton; + private javax.swing.JTextField tfDatasetName; + private javax.swing.JTextField tfDatasetVersion; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamManageTagDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamManageTagDialog.form new file mode 100644 index 0000000000..11b819de8d --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamManageTagDialog.form @@ -0,0 +1,101 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamManageTagDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamManageTagDialog.java new file mode 100644 index 0000000000..7d22f5afa0 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamManageTagDialog.java @@ -0,0 +1,217 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 - 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.stream.Collectors; +import javax.swing.JFrame; +import javax.swing.table.DefaultTableModel; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Instances of this class allow a user to select an existing hash database and + * add it to the set of hash databases used to classify files as unknown, known, + * or known bad. + */ +final class EamManageTagDialog extends javax.swing.JDialog { + + private static final Logger LOGGER = Logger.getLogger(EamManageTagDialog.class.getName()); + + + /** + * Displays a dialog that allows a user to select an existing hash database + * and add it to the set of hash databases used to classify files as + * unknown, known, or known bad. + */ + @Messages({"EnterpriseArtifactManagerManageTagDialog.title=Manage Correlation Tags"}) + EamManageTagDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), + Bundle.EnterpriseArtifactManagerManageTagDialog_title(), + true); // NON-NLS + initComponents(); + customizeComponents(); + display(); + } + + private void customizeComponents() { + //this.okButton.setEnabled(false); + EamDb dbManager = EamDb.getInstance(); + List badTags = dbManager.getBadTags(); + + List tagNames = new ArrayList<>(badTags); + try { + if (Case.isCaseOpen()) { + tagNames.addAll(Case.getCurrentCase().getServices().getTagsManager().getAllTagNames().stream() + .map(tag -> tag.getDisplayName()) + .filter(tagName -> !badTags.contains(tagName)) + .collect(Collectors.toList())); + } + } catch (TskCoreException ex) { + LOGGER.log(Level.WARNING, "Could not get list of tags in case", ex); + } + + Collections.sort(tagNames); + + DefaultTableModel model = (DefaultTableModel)tblTagNames.getModel(); + for (String tagName : tagNames) { + boolean enabled = badTags.contains(tagName); + model.addRow(new Object[]{ tagName, enabled }); + } + } + + private void display() { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + jScrollPane1 = new javax.swing.JScrollPane(); + tblTagNames = new javax.swing.JTable(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(EamManageTagDialog.class, "EamManageTagDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(EamManageTagDialog.class, "EamManageTagDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + tblTagNames.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "Tag", "Correlatable" + } + ) { + Class[] types = new Class [] { + java.lang.Object.class, java.lang.Boolean.class + }; + boolean[] canEdit = new boolean [] { + false, true + }; + + public Class getColumnClass(int columnIndex) { + return types [columnIndex]; + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit [columnIndex]; + } + }); + jScrollPane1.setViewportView(tblTagNames); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(0, 397, Short.MAX_VALUE) + .addComponent(okButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton)) + .addComponent(jScrollPane1)) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, okButton}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 335, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(okButton) + .addComponent(cancelButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + setBadTags(); + dispose(); + }//GEN-LAST:event_okButtonActionPerformed + + private void setBadTags() { + List badTags = new ArrayList<>(); + + DefaultTableModel model = (DefaultTableModel)tblTagNames.getModel(); + for (int i = 0; i < model.getRowCount(); ++i) { + String tagName = (String)model.getValueAt(i, 0); + boolean enabled = (boolean)model.getValueAt(i, 1); + + if (enabled) { + badTags.add(tagName); + } + } + EamDb dbManager = EamDb.getInstance(); + dbManager.setBadTags(badTags); + dbManager.saveSettings(); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.ButtonGroup buttonGroup1; + private javax.swing.JButton cancelButton; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JButton okButton; + private javax.swing.JTable tblTagNames; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamOptionsPanelController.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamOptionsPanelController.java new file mode 100644 index 0000000000..d7a364aa56 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamOptionsPanelController.java @@ -0,0 +1,141 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2014 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import javax.swing.JComponent; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import java.util.logging.Level; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.coreutils.Logger; + +/** + * Controller for the main settings panel + */ +@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_Enterprise_Artifact_Manager_Options", + iconBase = "org/sleuthkit/enterpriseartifactmanager/optionspanel/options-icon.png", + position = 14, + keywords = "#OptionsCategory_Keywords_Enterprise_Artifact_Manager_Options", + keywordsCategory = "EnterpriseArtifactManager") +public final class EamOptionsPanelController extends OptionsPanelController { + + private EamGlobalSettingsPanel panel; + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + private boolean changed; + private static final Logger LOGGER = Logger.getLogger(EamOptionsPanelController.class.getName()); + + @Override + public void update() { + getPanel().load(); + changed = false; + } + + @Override + public void applyChanges() { + getPanel().store(); + changed = false; + } + + @Override + public void cancel() { + } + + @Override + public boolean isValid() { + return getPanel().valid(); + } + + @Override + public boolean isChanged() { + return changed; + } + + @Override + public HelpCtx getHelpCtx() { + return null; + } + + @Override + public JComponent getComponent(Lookup masterLookup) { + return getPanel(); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener l) { + if (pcs.getPropertyChangeListeners().length == 0) { + pcs.addPropertyChangeListener(l); + } + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener l) { + /** + * Note the NetBeans Framework does not appear to call this at all. We + * are using NetBeans 7.3.1 Build 201306052037. Perhaps in a future + * version of the Framework this will be resolved, but for now, simply + * don't unregister anything and add one time only in the + * addPropertyChangeListener() method above. + */ + } + + private EamGlobalSettingsPanel getPanel() { + if (panel == null) { + panel = new EamGlobalSettingsPanel(); + panel.addPropertyChangeListener((PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) { + changed(); + } + }); + } + return panel; + } + + @Messages({"EnterpriseArtifactManagerOptionsController.moduleErr=Error processing value changes.", + "EnterpriseArtifactManagerOptionsController.moduleErr.msg=Value change processing failed."}) + void changed() { + if (!changed) { + changed = true; + + try { + pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true); + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "Error processing property change", ex); //NON-NLS + MessageNotifyUtil.Notify.show( + Bundle.EnterpriseArtifactManagerOptionsController_moduleErr(), + Bundle.EnterpriseArtifactManagerOptionsController_moduleErr_msg(), + MessageNotifyUtil.MessageType.ERROR); + } + } + + try { + pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error processing property change validation.", e); //NON-NLS + MessageNotifyUtil.Notify.show( + Bundle.EnterpriseArtifactManagerOptionsController_moduleErr(), + Bundle.EnterpriseArtifactManagerOptionsController_moduleErr_msg(), + MessageNotifyUtil.MessageType.ERROR); + } + } +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamPostgresSettingsDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamPostgresSettingsDialog.form new file mode 100644 index 0000000000..a61c8e59b5 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamPostgresSettingsDialog.form @@ -0,0 +1,246 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamPostgresSettingsDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamPostgresSettingsDialog.java new file mode 100644 index 0000000000..9c182f59c4 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamPostgresSettingsDialog.java @@ -0,0 +1,545 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.util.ArrayList; +import java.util.Collection; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.corecomponents.TextPrompt; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.PostgresEamDbSettings; + +/** + * Settings panel for the postgres-specific options + */ +public class EamPostgresSettingsDialog extends javax.swing.JDialog { + + private static final Logger LOGGER = Logger.getLogger(EamSqliteSettingsDialog.class.getName()); + private final ImageIcon goodIcon; + private final ImageIcon badIcon; + private final Collection textBoxes; + private final TextBoxChangedListener textBoxChangedListener; + + private final PostgresEamDbSettings dbSettings; + private Boolean hasChanged; + + /** + * Creates new form EnterpriseArtifactManagerPostgresSettingsDialog + */ + @NbBundle.Messages({"EnterpriseArtifactManagerPostgresSettingsDialog.postgresSettingsMessage.text=PostgreSQL Database Settings"}) + public EamPostgresSettingsDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), + Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_postgresSettingsMessage_text(), + true); // NON-NLS + + textBoxes = new ArrayList<>(); + textBoxChangedListener = new TextBoxChangedListener(); + this.dbSettings = new PostgresEamDbSettings(); + goodIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/good.png", false)); // NON-NLS + badIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/bad.png", false)); // NON-NLS + + initComponents(); + customizeComponents(); + display(); + } + + /** + * Let calling object determine if this dialog made any changes. + * + * @return true or false + */ + public Boolean isChanged() { + return hasChanged; + } + + private void customizeComponents() { + hasChanged = false; + lbTestDatabaseWarning.setText(""); + setTextPrompts(); + setTextBoxStatusIcons(); + setTextBoxListeners(); + tbDbHostname.setText(dbSettings.getHost()); + tbDbPort.setText(Integer.toString(dbSettings.getPort())); + tbDbName.setText(dbSettings.getDbName()); + tbDbUsername.setText(dbSettings.getUserName()); + tbDbPassword.setText(dbSettings.getPassword()); + + enableTestDatabaseButton(false); + enableSaveButton(false); + this.valid(); + } + + private void display() { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + + /** + * Add text prompts to all of the text fields. + */ + @Messages({"EnterpriseArtifactManagerPostgresSettingsDialog.textPrompt.hostnameOrIP=Hostname or IP Address", + "EnterpriseArtifactManagerPostgresSettingsDialog.textPrompt.port=Port Number", + "EnterpriseArtifactManagerPostgresSettingsDialog.textPrompt.dbName=Database Name", + "EnterpriseArtifactManagerPostgresSettingsDialog.textPrompt.user=Database User", + "EnterpriseArtifactManagerPostgresSettingsDialog.textPrompt.password=Database User's Password"}) + private void setTextPrompts() { + Collection textPrompts = new ArrayList<>(); + textPrompts.add(new TextPrompt(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_textPrompt_hostnameOrIP(), tbDbHostname)); + textPrompts.add(new TextPrompt(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_textPrompt_port(), tbDbPort)); + textPrompts.add(new TextPrompt(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_textPrompt_dbName(), tbDbName)); + textPrompts.add(new TextPrompt(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_textPrompt_user(), tbDbUsername)); + textPrompts.add(new TextPrompt(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_textPrompt_password(), tbDbPassword)); + configureTextPrompts(textPrompts); + } + + /** + * Set each textbox with a "statusIcon" property enabling the + * DocumentListeners to know which icon to erase when changes are made + */ + private void setTextBoxStatusIcons() { + tbDbHostname.getDocument().putProperty("statusIcon", lbTestDatabase); // NON-NLS + tbDbPort.getDocument().putProperty("statusIcon", lbTestDatabase); // NON-NLS + tbDbName.getDocument().putProperty("statusIcon", lbTestDatabase); // NON-NLS + tbDbUsername.getDocument().putProperty("statusIcon", lbTestDatabase); // NON-NLS + tbDbPassword.getDocument().putProperty("statusIcon", lbTestDatabase); // NON-NLS + } + + /** + * Register for notifications when the text boxes get updated. + */ + private void setTextBoxListeners() { + textBoxes.add(tbDbHostname); + textBoxes.add(tbDbPort); + textBoxes.add(tbDbName); + textBoxes.add(tbDbUsername); + textBoxes.add(tbDbPassword); + addDocumentListeners(textBoxes, textBoxChangedListener); + } + + /** + * Sets the foreground color and transparency of a collection of text + * prompts. + * + * @param textPrompts The text prompts to configure. + */ + private static void configureTextPrompts(Collection textPrompts) { + float alpha = 0.9f; // Mostly opaque + for (TextPrompt textPrompt : textPrompts) { + textPrompt.setForeground(Color.LIGHT_GRAY); + textPrompt.changeAlpha(alpha); + } + } + + /** + * Adds a change listener to a collection of text fields. + * + * @param textFields The text fields. + * @param listener The change listener. + */ + private static void addDocumentListeners(Collection textFields, TextBoxChangedListener listener) { + textFields.forEach((textField) -> { + textField.getDocument().addDocumentListener(listener); + }); + } + + /** + * Tests whether or not values have been entered in all of the database + * settings text fields. + * + * @return True or false. + */ + private boolean databaseFieldsArePopulated() { + return !tbDbHostname.getText().trim().isEmpty() + && !tbDbPort.getText().trim().isEmpty() + && !tbDbName.getText().trim().isEmpty() + && !tbDbUsername.getText().trim().isEmpty() + && !tbDbPassword.getText().trim().isEmpty(); + } + + /** + * Tests whether or not all of the settings components are populated. + * + * @return True or false. + */ + @Messages({"EnterpriseArtifactManagerPostgresSettingsDialog.validation.incompleteFields=Fill in all values"}) + private boolean checkFields() { + boolean result = true; + + boolean dbPopulated = databaseFieldsArePopulated(); + + if (!dbPopulated) { + // We don't even have everything filled out + result = false; + lbTestDatabaseWarning.setText(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_validation_incompleteFields()); + } + return result; + } + + /** + * Tests whether or not the database settings are valid. + * + * @return True or false. + */ + @Messages({"EnterpriseArtifactManagerPostgresSettingsDialog.validation.invalidHost=Invalid database hostname.", + "EnterpriseArtifactManagerPostgresSettingsDialog.validation.invalidPort=Invalid database port number.", + "EnterpriseArtifactManagerPostgresSettingsDialog.validation.invalidDbName=Invalid database name.", + "EnterpriseArtifactManagerPostgresSettingsDialog.validation.invalidDbUser=Invalid database username.", + "EnterpriseArtifactManagerPostgresSettingsDialog.validation.invalidDbPassword=Invalid database password.",}) + private boolean databaseSettingsAreValid() { + + try { + dbSettings.setHost(tbDbHostname.getText().trim()); + } catch (EamDbException ex) { + lbTestDatabaseWarning.setText(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_validation_invalidHost()); + return false; + } + + try { + dbSettings.setPort(Integer.valueOf(tbDbPort.getText().trim())); + } catch (NumberFormatException | EamDbException ex) { + lbTestDatabaseWarning.setText(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_validation_invalidPort()); + return false; + } + + try { + dbSettings.setDbName(tbDbName.getText().trim()); + } catch (EamDbException ex) { + lbTestDatabaseWarning.setText(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_validation_invalidDbName()); + return false; + } + + try { + dbSettings.setUserName(tbDbUsername.getText().trim()); + } catch (EamDbException ex) { + lbTestDatabaseWarning.setText(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_validation_invalidDbUser()); + return false; + } + + try { + dbSettings.setPassword(tbDbPassword.getText().trim()); + } catch (EamDbException ex) { + lbTestDatabaseWarning.setText(Bundle.EnterpriseArtifactManagerPostgresSettingsDialog_validation_invalidDbPassword()); + return false; + } + + return true; + } + + /** + * Validates that the form is filled out correctly for our usage. + * + * @return true if it's okay, false otherwise. + */ + public boolean valid() { + lbTestDatabaseWarning.setText(""); + + return checkFields() + && enableTestDatabaseButton(databaseSettingsAreValid()) + && enableSaveButton(databaseSettingsAreValid()); + } + + /** + * Enables the "Test Connection" button to test the database settings. + * + * @param enable + * + * @return True or False + */ + private boolean enableTestDatabaseButton(Boolean enable) { + bnTestConnection.setEnabled(enable); + return enable; + } + + /** + * Enables the "Save" button to save the database settings. + * + * @param enable + * + * @return True or False + */ + private boolean enableSaveButton(Boolean enable) { + bnSave.setEnabled(enable); + return enable; + } + + /** + * Used to listen for changes in text boxes. It lets the panel know things + * have been updated and that validation needs to happen. + */ + private class TextBoxChangedListener implements DocumentListener { + + @Override + public void changedUpdate(DocumentEvent e) { + Object statusIcon = e.getDocument().getProperty("statusIcon"); // NON-NLS + if (statusIcon != null) { + ((javax.swing.JLabel) statusIcon).setIcon(null); + } + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + Object statusIcon = e.getDocument().getProperty("statusIcon"); // NON-NLS + if (statusIcon != null) { + ((javax.swing.JLabel) statusIcon).setIcon(null); + } + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + Object statusIcon = e.getDocument().getProperty("statusIcon"); // NON-NLS + if (statusIcon != null) { + ((javax.swing.JLabel) statusIcon).setIcon(null); + } + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + pnOuter = new javax.swing.JPanel(); + jScrollPane = new javax.swing.JScrollPane(); + pnContent = new javax.swing.JPanel(); + lbHostName = new javax.swing.JLabel(); + lbPort = new javax.swing.JLabel(); + lbUserName = new javax.swing.JLabel(); + lbUserPassword = new javax.swing.JLabel(); + lbDatabaseName = new javax.swing.JLabel(); + tbDbHostname = new javax.swing.JTextField(); + tbDbPort = new javax.swing.JTextField(); + tbDbName = new javax.swing.JTextField(); + tbDbUsername = new javax.swing.JTextField(); + tbDbPassword = new javax.swing.JTextField(); + bnTestConnection = new javax.swing.JButton(); + bnSave = new javax.swing.JButton(); + bnCancel = new javax.swing.JButton(); + lbTestDatabaseWarning = new javax.swing.JLabel(); + lbTestDatabase = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(lbHostName, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.lbHostName.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbPort, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.lbPort.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbUserName, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.lbUserName.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbUserPassword, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.lbUserPassword.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(lbDatabaseName, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.lbDatabaseName.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnTestConnection, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.bnTestConnection.text")); // NOI18N + bnTestConnection.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnTestConnectionActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(bnSave, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.bnSave.text")); // NOI18N + bnSave.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnSaveActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(EamPostgresSettingsDialog.class, "EamPostgresSettingsDialog.bnCancel.text")); // NOI18N + bnCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnCancelActionPerformed(evt); + } + }); + + lbTestDatabaseWarning.setFont(new java.awt.Font("Tahoma", 1, 12)); // NOI18N + lbTestDatabaseWarning.setForeground(new java.awt.Color(255, 0, 0)); + + javax.swing.GroupLayout pnContentLayout = new javax.swing.GroupLayout(pnContent); + pnContent.setLayout(pnContentLayout); + pnContentLayout.setHorizontalGroup( + pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnContentLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnContentLayout.createSequentialGroup() + .addComponent(bnTestConnection) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(153, 153, 153) + .addComponent(bnSave) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(bnCancel)) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(lbTestDatabaseWarning, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, pnContentLayout.createSequentialGroup() + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbHostName) + .addComponent(lbPort) + .addComponent(lbDatabaseName) + .addComponent(lbUserName) + .addComponent(lbUserPassword)) + .addGap(18, 18, 18) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(tbDbPassword, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE) + .addComponent(tbDbUsername) + .addComponent(tbDbName) + .addComponent(tbDbHostname) + .addComponent(tbDbPort, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE))))) + .addGap(0, 11, Short.MAX_VALUE)) + ); + pnContentLayout.setVerticalGroup( + pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnContentLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(tbDbHostname, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbHostName, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(tbDbPort) + .addComponent(lbPort, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(tbDbName) + .addComponent(lbDatabaseName, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(tbDbUsername) + .addComponent(lbUserName, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbUserPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tbDbPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(37, 37, 37) + .addComponent(lbTestDatabaseWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnTestConnection) + .addComponent(bnSave) + .addComponent(bnCancel) + .addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE)) + ); + + jScrollPane.setViewportView(pnContent); + + javax.swing.GroupLayout pnOuterLayout = new javax.swing.GroupLayout(pnOuter); + pnOuter.setLayout(pnOuterLayout); + pnOuterLayout.setHorizontalGroup( + pnOuterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 448, Short.MAX_VALUE) + .addGroup(pnOuterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnOuterLayout.createSequentialGroup() + .addComponent(jScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 448, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + ); + pnOuterLayout.setVerticalGroup( + pnOuterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 249, Short.MAX_VALUE) + .addGroup(pnOuterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnOuterLayout.createSequentialGroup() + .addComponent(jScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 249, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pnOuter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pnOuter, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + + pack(); + }// //GEN-END:initComponents + + private void bnTestConnectionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestConnectionActionPerformed + lbTestDatabase.setIcon(null); + lbTestDatabase.setText(""); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + if (dbSettings.testSettings()) { + lbTestDatabase.setIcon(goodIcon); + } else { + lbTestDatabase.setIcon(badIcon); + } + + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + + }//GEN-LAST:event_bnTestConnectionActionPerformed + + private void bnSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnSaveActionPerformed + hasChanged = true; + dbSettings.setEnabled(true); + dbSettings.saveSettings(); + dispose(); + }//GEN-LAST:event_bnSaveActionPerformed + + private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed + dispose(); + }//GEN-LAST:event_bnCancelActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnCancel; + private javax.swing.JButton bnSave; + private javax.swing.JButton bnTestConnection; + private javax.swing.JScrollPane jScrollPane; + private javax.swing.JLabel lbDatabaseName; + private javax.swing.JLabel lbHostName; + private javax.swing.JLabel lbPort; + private javax.swing.JLabel lbTestDatabase; + private javax.swing.JLabel lbTestDatabaseWarning; + private javax.swing.JLabel lbUserName; + private javax.swing.JLabel lbUserPassword; + private javax.swing.JPanel pnContent; + private javax.swing.JPanel pnOuter; + private javax.swing.JTextField tbDbHostname; + private javax.swing.JTextField tbDbName; + private javax.swing.JTextField tbDbPassword; + private javax.swing.JTextField tbDbPort; + private javax.swing.JTextField tbDbUsername; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamSqliteSettingsDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamSqliteSettingsDialog.form new file mode 100644 index 0000000000..0f80f2de66 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamSqliteSettingsDialog.form @@ -0,0 +1,218 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamSqliteSettingsDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamSqliteSettingsDialog.java new file mode 100644 index 0000000000..bdeb79c0c7 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamSqliteSettingsDialog.java @@ -0,0 +1,366 @@ +/* + * Enterprise Artifact Manager + * + * Copyright 2015-2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import javax.swing.ImageIcon; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.ImageUtilities; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.SqliteEamDbSettings; + +/** + * Settings panel for the sqlite-specific options + */ +public class EamSqliteSettingsDialog extends javax.swing.JDialog { + + private static final Logger LOGGER = Logger.getLogger(EamSqliteSettingsDialog.class.getName()); + private final ImageIcon goodIcon; + private final ImageIcon badIcon; + private final TextBoxChangedListener textBoxChangedListener; + + private final SqliteEamDbSettings dbSettings; + private Boolean hasChanged; + + /** + * Creates new form EnterpriseArtifactManagerSQLiteSettingsDialog + */ + @Messages({"EnterpriseArtifactManagerSQLiteSettingsDialog.sqliteSettingsMessage.text=SQLite Database Settings"}) + public EamSqliteSettingsDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), + Bundle.EnterpriseArtifactManagerSQLiteSettingsDialog_sqliteSettingsMessage_text(), + true); // NON-NLS + + this.dbSettings = new SqliteEamDbSettings(); + goodIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/good.png", false)); // NON-NLS + badIcon = new ImageIcon(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/bad.png", false)); // NON-NLS + textBoxChangedListener = new TextBoxChangedListener(); + + initComponents(); + customizeComponents(); + valid(); + display(); + } + + /** + * Let calling object determine if this dialog made any changes. + * + * @return true or false + */ + public Boolean isChanged() { + return hasChanged; + } + + private void customizeComponents() { + customizeFileChooser(); + tfDatabasePath.setText(dbSettings.getFileNameWithPath()); + lbTestDatabaseWarning.setText(""); + hasChanged = false; + tfDatabasePath.getDocument().addDocumentListener(textBoxChangedListener); + bnSave.setEnabled(false); + bnTestDatabase.setEnabled(false); + } + + @Messages({"EnterpriseArtifactManagerSQLiteSettingsDialog.fileNameExtFilter.text=CDB Database File"}) + private void customizeFileChooser() { + fcDatabasePath.setDragEnabled(false); + fcDatabasePath.setFileSelectionMode(JFileChooser.FILES_ONLY); + String[] EXTENSION = new String[]{"db"}; //NON-NLS + FileNameExtensionFilter filter = new FileNameExtensionFilter(Bundle.EnterpriseArtifactManagerSQLiteSettingsDialog_fileNameExtFilter_text(), EXTENSION); // NON-NLS + fcDatabasePath.setFileFilter(filter); + fcDatabasePath.setMultiSelectionEnabled(false); + } + + private void display() { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + + /** + * Used to listen for changes in text boxes. It lets the panel know things + * have been updated and that validation needs to happen. + */ + private class TextBoxChangedListener implements DocumentListener { + + @Override + public void changedUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null); + valid(); + } + } + + private boolean valid() { + boolean result = false; + if (tfDatabasePath.getText().trim().isEmpty()) { + bnSave.setEnabled(false); + bnTestDatabase.setEnabled(false); + } else { + storeDbNameAndDirectory(); + bnSave.setEnabled(true); + bnTestDatabase.setEnabled(true); + result = true; + } + return result; + } + + /** + * Get the db file name and directory path from the file chooser and store + * in dbSettings. + */ + @Messages({"EnterpriseArtifactManagerSQLiteSettingsDialog.storeDbNameAndDirectory.failedToSetDbNamePathMsg=Database filename or directory path is missing. Try again."}) + private void storeDbNameAndDirectory() { + File databasePath = new File(tfDatabasePath.getText()); + try { + dbSettings.setDbName(databasePath.getName()); + dbSettings.setDbDirectory(databasePath.getParent()); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Failed to set database name or directory path.", ex); // NON-NLS + JOptionPane.showMessageDialog(this, Bundle.EnterpriseArtifactManagerSQLiteSettingsDialog_storeDbNameAndDirectory_failedToSetDbNamePathMsg()); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + fcDatabasePath = new javax.swing.JFileChooser(); + pnOuter = new javax.swing.JPanel(); + jScrollPane = new javax.swing.JScrollPane(); + pnContent = new javax.swing.JPanel(); + lbDatabasePath = new javax.swing.JLabel(); + tfDatabasePath = new javax.swing.JTextField(); + bnDatabasePathFileOpen = new javax.swing.JButton(); + lbTestDatabaseWarning = new javax.swing.JLabel(); + bnTestDatabase = new javax.swing.JButton(); + lbTestDatabase = new javax.swing.JLabel(); + bnCancel = new javax.swing.JButton(); + bnSave = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(lbDatabasePath, org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.lbDatabasePath.text")); // NOI18N + + tfDatabasePath.setText(org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.tfDatabasePath.text")); // NOI18N + tfDatabasePath.setToolTipText(org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.tfDatabasePath.toolTipText")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnDatabasePathFileOpen, org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.bnDatabasePathFileOpen.text")); // NOI18N + bnDatabasePathFileOpen.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnDatabasePathFileOpenActionPerformed(evt); + } + }); + + lbTestDatabaseWarning.setForeground(new java.awt.Color(255, 51, 51)); + org.openide.awt.Mnemonics.setLocalizedText(lbTestDatabaseWarning, org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.lbTestDatabaseWarning.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnTestDatabase, org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.bnTestDatabase.text")); // NOI18N + bnTestDatabase.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnTestDatabaseActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(lbTestDatabase, org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.lbTestDatabase.text")); // NOI18N + lbTestDatabase.setName(""); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(bnCancel, org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.bnCancel.text")); // NOI18N + bnCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnCancelActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(bnSave, org.openide.util.NbBundle.getMessage(EamSqliteSettingsDialog.class, "EamSqliteSettingsDialog.bnSave.text")); // NOI18N + bnSave.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bnSaveActionPerformed(evt); + } + }); + + javax.swing.GroupLayout pnContentLayout = new javax.swing.GroupLayout(pnContent); + pnContent.setLayout(pnContentLayout); + pnContentLayout.setHorizontalGroup( + pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnContentLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnContentLayout.createSequentialGroup() + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(lbTestDatabaseWarning, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(pnContentLayout.createSequentialGroup() + .addComponent(lbDatabasePath) + .addGap(18, 18, 18) + .addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, 343, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 57, Short.MAX_VALUE) + .addComponent(bnDatabasePathFileOpen) + .addGap(24, 24, 24)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnContentLayout.createSequentialGroup() + .addComponent(bnTestDatabase) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(bnSave) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(bnCancel) + .addContainerGap()))) + ); + pnContentLayout.setVerticalGroup( + pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnContentLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lbDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(tfDatabasePath, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(bnDatabasePathFileOpen)) + .addGap(18, 18, 18) + .addComponent(lbTestDatabaseWarning, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnContentLayout.createSequentialGroup() + .addGap(19, 19, 19) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnCancel) + .addComponent(bnSave))) + .addGroup(pnContentLayout.createSequentialGroup() + .addGap(18, 18, 18) + .addGroup(pnContentLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbTestDatabase, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(bnTestDatabase)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + jScrollPane.setViewportView(pnContent); + + javax.swing.GroupLayout pnOuterLayout = new javax.swing.GroupLayout(pnOuter); + pnOuter.setLayout(pnOuterLayout); + pnOuterLayout.setHorizontalGroup( + pnOuterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane) + ); + pnOuterLayout.setVerticalGroup( + pnOuterLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 603, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pnOuter, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 129, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(pnOuter, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + ); + + pack(); + }// //GEN-END:initComponents + + @Messages({"EnterpriseArtifactManagerSQLiteSettingsDialog.chooserPath.failedToGetDbPathMsg=Selected database path is invalid. Try again."}) + private void bnDatabasePathFileOpenActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnDatabasePathFileOpenActionPerformed + if (fcDatabasePath.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + File databaseFile = fcDatabasePath.getSelectedFile(); + try { + tfDatabasePath.setText(databaseFile.getCanonicalPath()); + valid(); + + } catch (IOException ex) { + LOGGER.log(Level.SEVERE, "Failed to get path of selected database file", ex); // NON-NLS + JOptionPane.showMessageDialog(this, Bundle.EnterpriseArtifactManagerSQLiteSettingsDialog_chooserPath_failedToGetDbPathMsg()); + } + } + }//GEN-LAST:event_bnDatabasePathFileOpenActionPerformed + + private void bnTestDatabaseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestDatabaseActionPerformed + lbTestDatabase.setIcon(null); + lbTestDatabaseWarning.setText(""); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + if (dbSettings.testSettings()) { + lbTestDatabase.setIcon(goodIcon); + } else { + lbTestDatabase.setIcon(badIcon); + } + + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + }//GEN-LAST:event_bnTestDatabaseActionPerformed + + private void bnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnCancelActionPerformed + dispose(); + }//GEN-LAST:event_bnCancelActionPerformed + + private void bnSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnSaveActionPerformed + hasChanged = true; + dbSettings.setEnabled(true); + dbSettings.saveSettings(); + dispose(); + }//GEN-LAST:event_bnSaveActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton bnCancel; + private javax.swing.JButton bnDatabasePathFileOpen; + private javax.swing.JButton bnSave; + private javax.swing.JButton bnTestDatabase; + private javax.swing.JFileChooser fcDatabasePath; + private javax.swing.JScrollPane jScrollPane; + private javax.swing.JLabel lbDatabasePath; + private javax.swing.JLabel lbTestDatabase; + private javax.swing.JLabel lbTestDatabaseWarning; + private javax.swing.JPanel pnContent; + private javax.swing.JPanel pnOuter; + private javax.swing.JTextField tfDatabasePath; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamTypesSelectionDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamTypesSelectionDialog.form new file mode 100644 index 0000000000..c507645082 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamTypesSelectionDialog.form @@ -0,0 +1,131 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamTypesSelectionDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamTypesSelectionDialog.java new file mode 100644 index 0000000000..a3c4ff8a87 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/EamTypesSelectionDialog.java @@ -0,0 +1,265 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011 - 2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.optionspanel; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import javax.swing.JFrame; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableModel; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle.Messages; +import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamArtifact; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDbException; +import org.sleuthkit.autopsy.experimental.enterpriseartifactmanager.datamodel.EamDb; + +/** + * Dialog to handle management of correlation types handled by the enterprise + * artifact manager + */ +final class EamTypesSelectionDialog extends javax.swing.JDialog { + + private static final Logger LOGGER = Logger.getLogger(EamManageTagDialog.class.getName()); + + private final List eamArtifactTypes; + + /** + * Displays a dialog that allows a user to select which Type(s) should be + * used for Correlation during ingest. + */ + @Messages({"EnterpriseArtifactManagerTypesSelectionDialog.title=Correlation Types Selections", + "EnterpriseArtifactManagerTypesSelectionDialog.instructions.text=Select one or more Type's to use for Correlation during Ingest."}) + EamTypesSelectionDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), + Bundle.EnterpriseArtifactManagerTypesSelectionDialog_title(), + true); // NON-NLS + this.eamArtifactTypes = new ArrayList<>(); + initComponents(); + customizeComponents(); + display(); + valid(); + } + + private void customizeComponents() { + enableOkButton(false); + lbInstructions.setText(Bundle.EnterpriseArtifactManagerTypesSelectionDialog_instructions_text()); + lbWarningMsg.setText(""); + + loadData(); + } + + private void loadData() { + DefaultTableModel model = (DefaultTableModel) tbCorrelatableTypes.getModel(); + try { + EamDb dbManager = EamDb.getInstance(); + eamArtifactTypes.clear(); + eamArtifactTypes.addAll(dbManager.getCorrelationArtifactTypes()); + } catch (EamDbException ex) { + Exceptions.printStackTrace(ex); + } + + eamArtifactTypes.forEach((aType) -> { + model.addRow(new Object[]{aType.getName(), aType.isEnabled()}); + }); + model.addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent evt) { + int row = evt.getFirstRow(); + TableModel model = (TableModel) evt.getSource(); + Object typeName = model.getValueAt(row, 0); + Boolean enabled = (Boolean) model.getValueAt(row, 1); + + eamArtifactTypes.stream().filter((aType) -> (aType.getName().equals(typeName))).forEachOrdered((aType) -> { + aType.setEnabled(enabled); + }); + valid(); + } + }); + } + + private void display() { + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenDimension.width - getSize().width) / 2, (screenDimension.height - getSize().height) / 2); + setVisible(true); + } + + @Messages({"EnterpriseArtifactManagerTypesSelectionDialog.noneSelected=Must enable at least 1 Type."}) + private boolean valid() { + lbWarningMsg.setText(""); + + int countEnabled = 0; + countEnabled = eamArtifactTypes.stream().filter((aType) -> (aType.isEnabled())).map((_item) -> 1).reduce(countEnabled, Integer::sum); + + if (0 == countEnabled) { + lbWarningMsg.setText(Bundle.EnterpriseArtifactManagerTypesSelectionDialog_noneSelected()); + return enableOkButton(false); + } + return enableOkButton(true); + } + + private boolean enableOkButton(boolean enable) { + okButton.setEnabled(enable); + return enable; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonGroup1 = new javax.swing.ButtonGroup(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + jScrollPane1 = new javax.swing.JScrollPane(); + tbCorrelatableTypes = new javax.swing.JTable(); + lbInstructions = new javax.swing.JLabel(); + lbWarningMsg = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(EamTypesSelectionDialog.class, "CorrelationEngineManageTagDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(EamTypesSelectionDialog.class, "CorrelationEngineManageTagDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + tbCorrelatableTypes.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "Correlation Type", "Enable" + } + ) { + Class[] types = new Class [] { + java.lang.String.class, java.lang.Boolean.class + }; + boolean[] canEdit = new boolean [] { + false, true + }; + + public Class getColumnClass(int columnIndex) { + return types [columnIndex]; + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit [columnIndex]; + } + }); + jScrollPane1.setViewportView(tbCorrelatableTypes); + + org.openide.awt.Mnemonics.setLocalizedText(lbInstructions, org.openide.util.NbBundle.getMessage(EamTypesSelectionDialog.class, "CorrelationEngineManageTagDialog.lbInstructions.text")); // NOI18N + + lbWarningMsg.setFont(new java.awt.Font("Tahoma", 0, 12)); // NOI18N + lbWarningMsg.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(lbWarningMsg, org.openide.util.NbBundle.getMessage(EamTypesSelectionDialog.class, "CorrelationEngineManageTagDialog.lbWarningMsg.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lbWarningMsg, javax.swing.GroupLayout.PREFERRED_SIZE, 158, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(okButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton)) + .addComponent(lbInstructions, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 316, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {cancelButton, okButton}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(lbInstructions, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 306, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbWarningMsg, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(okButton) + .addComponent(cancelButton))) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + @Messages({"EnterpriseArtifactManagerTypesSelectionDialog.okbutton.failure=Error saving updated selections."}) + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + + if (0 == eamArtifactTypes.size()) { + dispose(); + } else { + EamDb dbManager = EamDb.getInstance(); + eamArtifactTypes.forEach((aType) -> { + try { + dbManager.updateCorrelationArtifactType(aType); + dispose(); + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Failed to updated Enterprise Artifact Manager Artifact Types with selections from Dialog.", ex); // NON-NLS + lbWarningMsg.setText(Bundle.EnterpriseArtifactManagerTypesSelectionDialog_okbutton_failure()); + } + }); + } + }//GEN-LAST:event_okButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.ButtonGroup buttonGroup1; + private javax.swing.JButton cancelButton; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JLabel lbInstructions; + private javax.swing.JLabel lbWarningMsg; + private javax.swing.JButton okButton; + private javax.swing.JTable tbCorrelatableTypes; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/import16.png b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/import16.png new file mode 100644 index 0000000000..18b35db444 Binary files /dev/null and b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/import16.png differ diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/options-icon.png b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/options-icon.png new file mode 100644 index 0000000000..e3ca571e32 Binary files /dev/null and b/Experimental/src/org/sleuthkit/autopsy/experimental/enterpriseartifactmanager/optionspanel/options-icon.png differ