diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index d82b79fe8c..2d59333004 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -128,6 +128,10 @@ MultiUserSettingsPanel.lbTestResultText.text= MultiUserSettingsPanel.outputPathTextField.toolTipText=The location where case folder will be created for the test case. MultiUserSettingsPanel.outputPathTextField.text= MultiUserSettingsPanel.browseOutputFolderButton.text=Browse +MultiUserSettingsPanel.validationErrMsg.outputPathNotSpecified=Output folder must be set +MultiUserSettingsPanel.PathInvalid=Path is not valid +MultiUserSettingsPanel.CannotAccess=Cannot access +MultiUserSettingsPanel.CheckPermissions=Check permissions. AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText= AutopsyOptionsPanel.agencyLogoPathField.text= SortChooserDialog.label=remove diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED index 9de7debe1b..179fe5e186 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties-MERGED @@ -180,6 +180,7 @@ MultiUserSettingsPanel.lbTestResultText.text= MultiUserSettingsPanel.outputPathTextField.toolTipText=The location where case folder will be created for the test case. MultiUserSettingsPanel.outputPathTextField.text= MultiUserSettingsPanel.browseOutputFolderButton.text=Browse +MultiUserSettingsPanel.validationErrMsg.outputPathNotSpecified=Output directory not specified AutopsyOptionsPanel.agencyLogoImageLabel.toolTipText= AutopsyOptionsPanel.agencyLogoPathField.text= SortChooserDialog.label=remove diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form index ec1c390833..8c2f60a8bc 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.form @@ -53,7 +53,7 @@ - + @@ -66,13 +66,11 @@ + - - - - - + + @@ -591,30 +589,26 @@ - + - - + + - - + + + + + + + + + + + + - - - - - - - - - - - - - - @@ -622,25 +616,21 @@ - - - - - + + + + + - - - - - - - - - - + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java index 26d2d52018..09f5b763bb 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserSettingsPanel.java @@ -32,12 +32,15 @@ import org.sleuthkit.autopsy.events.MessageServiceConnectionInfo; import org.sleuthkit.autopsy.coreutils.Logger; import java.awt.Cursor; import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.logging.Level; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.sleuthkit.autopsy.core.UserPreferencesException; +import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.events.MessageServiceException; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; @@ -472,44 +475,38 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnTestMultiUserLayout.createSequentialGroup() .addContainerGap() - .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnTestMultiUserLayout.createSequentialGroup() - .addComponent(lbTestMultiUserText) - .addGap(176, 176, 176)) + .addComponent(lbTestResultText, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) .addGroup(pnTestMultiUserLayout.createSequentialGroup() - .addComponent(lbTestResultText) - .addGap(336, 336, 336))) - .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(browseOutputFolderButton, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addComponent(bnTestMultiUser, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(lbMultiUserResult, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(31, Short.MAX_VALUE)) - .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(pnTestMultiUserLayout.createSequentialGroup() - .addContainerGap() - .addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 312, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(139, Short.MAX_VALUE))) + .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 284, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lbTestMultiUserText, javax.swing.GroupLayout.Alignment.LEADING)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(browseOutputFolderButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(bnTestMultiUser, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbMultiUserResult, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(35, 35, 35)))) ); pnTestMultiUserLayout.setVerticalGroup( pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(pnTestMultiUserLayout.createSequentialGroup() .addContainerGap() - .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(lbTestMultiUserText) - .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lbMultiUserResult, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(bnTestMultiUser))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(browseOutputFolderButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 22, Short.MAX_VALUE) + .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lbMultiUserResult, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(bnTestMultiUser) + .addComponent(lbTestMultiUserText))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(browseOutputFolderButton) + .addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) .addComponent(lbTestResultText, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) - .addGroup(pnTestMultiUserLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(pnTestMultiUserLayout.createSequentialGroup() - .addGap(56, 56, 56) - .addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(56, Short.MAX_VALUE))) + .addGap(0, 12, Short.MAX_VALUE)) ); javax.swing.GroupLayout pnOverallPanelLayout = new javax.swing.GroupLayout(pnOverallPanel); @@ -528,7 +525,7 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(pnMessagingSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pnTestMultiUser, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(765, Short.MAX_VALUE)) + .addContainerGap(822, Short.MAX_VALUE)) ); pnOverallPanelLayout.setVerticalGroup( pnOverallPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -538,11 +535,10 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { .addComponent(cbEnableMultiUser)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(pnOverallPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pnTestMultiUser, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(pnOverallPanelLayout.createSequentialGroup() - .addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pnSolrSettings, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(pnDatabaseSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pnTestMultiUser, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pnSolrSettings, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(pnMessagingSettings, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap(206, Short.MAX_VALUE)) @@ -677,21 +673,53 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { private void bnTestMultiUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnTestMultiUserActionPerformed + // ELTODO + // should we run tests for all services? + + String resultsFolderPath = getNormalizedFolderPath(outputPathTextField.getText().trim()); + if (resultsFolderPath.isEmpty()) { + lbMultiUserResult.setIcon(badIcon); + lbTestResultText.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.validationErrMsg.outputPathNotSpecified")); + lbTestResultText.setForeground(Color.RED); + return; + } + + if (!isFolderPathValid(resultsFolderPath)) { + lbMultiUserResult.setIcon(badIcon); + lbTestResultText.setForeground(Color.RED); + lbTestResultText.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.PathInvalid")); + return; + } + + if (false == permissionsAppropriate(resultsFolderPath)) { + lbMultiUserResult.setIcon(badIcon); + lbTestResultText.setForeground(Color.RED); + lbTestResultText.setText(NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.CannotAccess") + + " " + resultsFolderPath + " " + + NbBundle.getMessage(MultiUserSettingsPanel.class, "MultiUserSettingsPanel.CheckPermissions")); + return; + } + // save the configuration that user has entered in other fields (i.e. Solr, Postgres, ActiveMQ). // we need it stored in the settings file in order to create a case and connect to database/kws. store(); - String resultsFolderPath = getNormalizedFolderPath(outputPathTextField.getText().trim()); + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); String testResult = MultiUserTestTool.runTest(resultsFolderPath); + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); if (testResult.equals(MultiUserTestTool.RESULT_SUCCESS)) { // test successful lbMultiUserResult.setIcon(goodIcon); lbTestResultText.setText(testResult); + lbTestResultText.setForeground(Color.BLACK); } else { // test failed lbMultiUserResult.setIcon(badIcon); lbTestResultText.setText(testResult); + lbTestResultText.setForeground(Color.RED); } + + // ELTODO investigate ingest module startup popup when KWS wasn't able to load core (e.g. empty outputPathTextField) }//GEN-LAST:event_bnTestMultiUserActionPerformed /** @@ -747,8 +775,46 @@ public final class MultiUserSettingsPanel extends javax.swing.JPanel { valid(); controller.changed(); } + + // ELTODO + // save output directory to storage and reload it next time }//GEN-LAST:event_browseOutputFolderButtonActionPerformed + /** + * Validates that a path is valid and points to a folder. + * + * @param path A path to be validated + * + * @return boolean returns true if valid and points to a folder, false + * otherwise + */ + boolean isFolderPathValid(String path) { + try { + File file = new File(normalizePath(path)); + + // check if it's a symbolic link + if (Files.isSymbolicLink(file.toPath())) { + return true; + } + + // local folder + if (file.exists() && file.isDirectory()) { + return true; + } + } catch (Exception ex) { + // Files.isSymbolicLink (and other "files" methods) throw exceptions on seemingly innocent inputs. + // For example, it will throw an exception when either " " is last character in path or + // a path starting with ":". + // We can just ignore these exceptions as they occur in process of user typing in the path. + return false; + } + return false; + } + + boolean permissionsAppropriate(String path) { + return FileUtil.hasReadWriteAccess(Paths.get(path)); + } + void load() { lbTestDatabase.setIcon(null); lbTestSolr.setIcon(null); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserTestTool.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserTestTool.java index 56c71967b6..039ea40c27 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserTestTool.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MultiUserTestTool.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.UUID; import java.util.logging.Level; import org.apache.commons.io.FileUtils; +import org.openide.util.Lookup; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseDetails; @@ -44,12 +45,15 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.casemodule.LocalFilesDSProcessor; +import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestJobStartResult; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestModuleError; +import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; +import org.sleuthkit.datamodel.AbstractFile; /** * Test tool that creates a multi user case, database, KWS index, runs ingest, @@ -60,14 +64,14 @@ class MultiUserTestTool { private static final String CASE_NAME = "Test_MU_Settings"; private static final Logger LOGGER = Logger.getLogger(MultiUserTestTool.class.getName()); + private static final String TEST_FILE_NAME = "Test.txt"; + private static final Object INGEST_LOCK = new Object(); static final String RESULT_SUCCESS = "Success"; - private static final Object INGEST_LOCK = new Object(); - static String runTest(String rootOutputDirectory) { - // 1 (MH) Creates a case in the output folder. Could be hard coded name/time stamp thing. + // Create a case in the output folder. Case caseForJob; try { caseForJob = createCase(CASE_NAME, rootOutputDirectory); @@ -82,15 +86,7 @@ class MultiUserTestTool { } try { - // 2 (MH) Verifies that Solr was able to create the core. If any of those steps fail, it gives an error message. - /*KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW, "Fake Keyword Search", "Fake Keyword Preview Text")); - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH, "Output Path", rootOutputDirectory)); - BlackboardArtifact bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); - bba.addAttributes(attributes);*/ - - // Verifies that DB was created. etc + // Verify that DB was created. etc String getDatabaseInfoQuery = "select * from tsk_db_info"; try (SleuthkitCase.CaseDbQuery queryResult = caseForJob.getSleuthkitCase().executeQuery(getDatabaseInfoQuery)) { ResultSet resultSet = queryResult.getResultSet(); @@ -104,8 +100,8 @@ class MultiUserTestTool { return "Unable to read from case database"; } - // 3 (NTH) Makes a text file in a temp folder with just the text "Test" in it. - String tempFilePath = caseForJob.getTempDirectory() + File.separator + "Test.txt"; + // Make a text file in a temp folder with just the text "Test" in it. + String tempFilePath = caseForJob.getTempDirectory() + File.separator + TEST_FILE_NAME; try { FileUtils.writeStringToFile(new File(tempFilePath), "Test", Charset.forName("UTF-8")); } catch (IOException ex) { @@ -113,7 +109,7 @@ class MultiUserTestTool { return "Unable to create a file in case output directory"; } - // 4 (NTH) Adds it as a logical file set data source. + // Add it as a logical file set data source. AutoIngestDataSource dataSource = new AutoIngestDataSource("", Paths.get(tempFilePath)); try { String error = runLogicalFilesDSP(caseForJob, dataSource); @@ -121,13 +117,43 @@ class MultiUserTestTool { LOGGER.log(Level.SEVERE, error); return error; } + + // ELTODO DELETE + dataSource = new AutoIngestDataSource("", Paths.get("C:\\TEST\\Inputs\\Test archivedsp\\Test 6.zip")); + error = runLogicalFilesDSP(caseForJob, dataSource); } catch (InterruptedException ex) { LOGGER.log(Level.SEVERE, "Unable to add test file as data source to case", ex); return "Unable to add test file as data source to case"; } + // Verify that Solr was able to create the core and is able to write to it + FileManager fileManager = caseForJob.getServices().getFileManager(); + AbstractFile file = null; + List listOfFiles = null; + try { + listOfFiles = fileManager.findFiles(TEST_FILE_NAME); + if (listOfFiles == null || listOfFiles.isEmpty()) { + LOGGER.log(Level.SEVERE, "Unable to read test file info from case database"); + return "Unable to read test file info from case database"; + } + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Unable to read test file info from case database", ex); + return "Unable to read test file info from case database"; + } + + file = listOfFiles.get(0); + + // write to KWS index + KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class); + try { + kwsService.index(file); + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Unable to write to Keword Search index", ex); + return "Unable to write to Keword Search index"; + } + + // Run ingest on that data source and report errors if the modules could not start. try { - // 5 (NTH) Runs ingest on that data source and reports errors if the modules could not start. String error = analyze(dataSource); if (!error.isEmpty()) { LOGGER.log(Level.SEVERE, error); @@ -139,13 +165,13 @@ class MultiUserTestTool { } //} catch (Throwable ex) { } finally { - // 6 (MH) Close and delete the case. - try { + // Close and delete the case. + /* ELTODO try { Case.deleteCurrentCase(); } catch (CaseActionException ex) { LOGGER.log(Level.SEVERE, "Unable to delete test case", ex); return "Unable to delete test case"; - } + } */ } return RESULT_SUCCESS;