diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties index f3081bef89..4de3b58397 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties @@ -257,3 +257,7 @@ CasesDashboardTopComponent.refreshButton.text=Refresh AutoIngestCasesDeletionDialog.jLabel1.text=Progress CasesDashboardTopComponent.deleteOrphanCaseNodesButton.text=Delete Orphan Case Znodes CasesDashboardTopComponent.deleteOrphanManifestNodesButton.text=Delete Orphan Manifest Znodes +DeleteOrphanCaseNodesDialog.descriptionText.text=The following znodes are no longer have an associated case. Would you like to delete them? +DeleteOrphanCaseNodesDialog.okButton.text=OK +DeleteOrphanCaseNodesDialog.cancelButton.text=Cancel +DeleteOrphanCaseNodesDialog.titleText.text=Delete The Following Znodes? diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesDialog.form b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesDialog.form new file mode 100644 index 0000000000..af67571b2d --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesDialog.form @@ -0,0 +1,123 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesDialog.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesDialog.java new file mode 100644 index 0000000000..76ef98d590 --- /dev/null +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesDialog.java @@ -0,0 +1,156 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020-2020 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.autoingest; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * This dialog shows the system administrator the orphaned znodes to be deleted. + * If 'OK' is selected, isOkSelected() will return true. + */ +public class DeleteOrphanCaseNodesDialog extends javax.swing.JDialog { + private static final String NEW_LINE = System.getProperty("line.separator"); + private static final String DEFAULT_INDENT = " "; + private static final String COLON = ":"; + + private boolean okSelected = false; + + /** + * Creates new form DeleteOrphanCaseNodesDialog + */ + public DeleteOrphanCaseNodesDialog(java.awt.Frame parent, boolean modal, Map> znodes) { + super(parent, modal); + initComponents(); + additionalInit(znodes); + } + + private void additionalInit(Map> znodes) { + String textAreaText = ""; + if (znodes != null) { + textAreaText = znodes.entrySet().stream().map((kv) -> { + return Stream.concat( + Stream.of(kv.getKey() + COLON), + kv.getValue().stream().map((node) -> DEFAULT_INDENT + node)) + .collect(Collectors.joining(NEW_LINE)); + }) + .collect(Collectors.joining(NEW_LINE)); + } + + znodesTextArea.setText(textAreaText); + } + + + /** + * 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() { + + javax.swing.JLabel descriptionText = new javax.swing.JLabel(); + javax.swing.JScrollPane jScrollPane = new javax.swing.JScrollPane(); + znodesTextArea = new javax.swing.JTextArea(); + javax.swing.JButton cancelButton = new javax.swing.JButton(); + javax.swing.JButton okButton = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(org.openide.util.NbBundle.getMessage(DeleteOrphanCaseNodesDialog.class, "DeleteOrphanCaseNodesDialog.titleText.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(descriptionText, org.openide.util.NbBundle.getMessage(DeleteOrphanCaseNodesDialog.class, "DeleteOrphanCaseNodesDialog.descriptionText.text")); // NOI18N + descriptionText.setVerticalAlignment(javax.swing.SwingConstants.TOP); + + znodesTextArea.setEditable(false); + znodesTextArea.setColumns(20); + znodesTextArea.setLineWrap(true); + znodesTextArea.setRows(5); + jScrollPane.setViewportView(znodesTextArea); + + org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(DeleteOrphanCaseNodesDialog.class, "DeleteOrphanCaseNodesDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(DeleteOrphanCaseNodesDialog.class, "DeleteOrphanCaseNodesDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + 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) + .addComponent(jScrollPane, javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(descriptionText, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(okButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(descriptionText) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 238, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cancelButton) + .addComponent(okButton)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + okSelected = true; + dispose(); + }//GEN-LAST:event_okButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed + dispose(); + }//GEN-LAST:event_cancelButtonActionPerformed + + /** + * If the system administrator selected OK. + * @return Whether or not 'OK' was selected by the system administrator. + */ + public boolean isOkSelected() { + return okSelected; + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextArea znodesTextArea; + // End of variables declaration//GEN-END:variables +} diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesTask.java index d13761f9b8..5521f46943 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteOrphanCaseNodesTask.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019-2019 Basis Technology Corp. + * Copyright 2019-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,11 +23,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils; @@ -61,6 +59,10 @@ final class DeleteOrphanCaseNodesTask implements Runnable { } + /** + * Retrieves an instance of the coordination service in order to fetch znodes and potentially delete. + * @return The coordination service or null on error. + */ private CoordinationService getCoordinationService() { progress.progress(Bundle.DeleteOrphanCaseNodesTask_progress_connectingToCoordSvc()); logger.log(Level.INFO, Bundle.DeleteOrphanCaseNodesTask_progress_connectingToCoordSvc()); @@ -74,6 +76,11 @@ final class DeleteOrphanCaseNodesTask implements Runnable { } + /** + * Retrieves node paths for cases. + * @param coordinationService The coordination service to use in order to fetch the node paths. + * @return The list of node paths for cases. + */ private List getNodePaths(CoordinationService coordinationService) { progress.progress(Bundle.DeleteOrphanCaseNodesTask_progress_gettingCaseZnodes()); logger.log(Level.INFO, Bundle.DeleteOrphanCaseNodesTask_progress_gettingCaseZnodes()); @@ -98,6 +105,11 @@ final class DeleteOrphanCaseNodesTask implements Runnable { paths.add(path); } + /** + * Determines orphaned znode paths. + * @param nodePaths The list of case node paths. + * @return The list of orphaned node paths. + */ private Map> getOrphanedNodes(List nodePaths) { progress.progress(Bundle.DeleteOrphanCaseNodesTask_progress_lookingForOrphanedCaseZnodes()); logger.log(Level.INFO, Bundle.DeleteOrphanCaseNodesTask_progress_lookingForOrphanedCaseZnodes()); @@ -125,6 +137,16 @@ final class DeleteOrphanCaseNodesTask implements Runnable { return nodePathsToDelete; } + /** + * prompts the user with a list of orphaned znodes. + * @param orphanedNodes The orphaned znodes. + * @return True if the user would like to proceed deleting the znodes. + */ + private boolean promptUser(Map> orphanedNodes) { + DeleteOrphanCaseNodesDialog dialog = new DeleteOrphanCaseNodesDialog(null, true, orphanedNodes); + return dialog.isOkSelected(); + } + @Override @@ -147,7 +169,7 @@ final class DeleteOrphanCaseNodesTask implements Runnable { Map> orphanedNodes = getOrphanedNodes(nodePaths); - boolean continueDelete = displayToUser(orphanedNodes); + boolean continueDelete = promptUser(orphanedNodes); if (continueDelete) deleteNodes(coordinationService, orphanedNodes); @@ -169,6 +191,14 @@ final class DeleteOrphanCaseNodesTask implements Runnable { } + /** + * Deletes the orphaned znodes provided in the 'orphanedNodes' variable. + * @param coordinationService The coordination service to use for deletion. + * @param orphanedNodes A mapping of case to the orphaned znodes. + * + * @throws InterruptedException If the thread executing this task is + * interrupted during the delete operation. + */ private void deleteNodes(CoordinationService coordinationService, Map> orphanedNodes) { String caseName = null; String nodePath = null;