integrate and update data src deletion

This commit is contained in:
Richard Cordovano 2019-10-30 16:42:20 -04:00
parent 37fb384192
commit 8db2acfe16
8 changed files with 336 additions and 261 deletions

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule;
package org.sleuthkit.autopsy.access;
import java.io.File;
import java.nio.file.Paths;
@ -26,7 +26,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
* Class for methods to check if access should be limited to a feature
*
*/
final class AccessLimiterUtils {
final public class AccessLimiterUtils {
private final static String MULTI_USER_ACCESS_FILE_NAME = "mualimit"; // NON-NLS
private final static String MULTI_USER_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), MULTI_USER_ACCESS_FILE_NAME).toString();
@ -36,7 +36,7 @@ final class AccessLimiterUtils {
*
* @return True if privileges should be restricted, false otherwise.
*/
static boolean limitMultiUserAccess() {
public static boolean limitMultiUserAccess() {
return new File(MULTI_USER_ACCESS_FILE_PATH).exists();
}

View File

@ -23,7 +23,6 @@ DeleteContentTagAction.deleteTag=Remove Selected Tag(s)
DeleteContentTagAction.tagDelErr=Tag Deletion Error
# {0} - tagName
DeleteContentTagAction.unableToDelTag.msg=Unable to delete tag {0}.
DeleteDataSourceAction.name.text=Delete Data Source
DeleteFileBlackboardArtifactTagAction.deleteTag=Remove Result Tag
# {0} - artifactID
DeleteFileBlackboardArtifactTagAction.deleteTag.alert=Unable to untag artifact {0}.

View File

@ -1,78 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.actions;
import java.awt.event.ActionEvent;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService;
import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException;
import org.sleuthkit.datamodel.TskCoreException;
/**
* An Action that allows a user to delete a data source.
*/
public final class DeleteDataSourceAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName());
private long dataSourceID;
/**
* Constructs an Action that allows a user to delete a data source.
*
* @param dataSourceID The object ID of the data source to be deleted.
*/
@NbBundle.Messages({
"DeleteDataSourceAction.name.text=Delete Data Source"
})
public DeleteDataSourceAction(Long dataSourceID) {
super(Bundle.DeleteDataSourceAction_name_text());
this.dataSourceID = dataSourceID;
}
@Override
public void actionPerformed(ActionEvent event) {
// try {
// Case.getCurrentCaseThrows().getSleuthkitCase().deleteDataSource(dataSourceID);
// KeywordSearchService kwsService = Lookup.getDefault().lookup(KeywordSearchService.class);
// kwsService.deleteDataSource(dataSourceID);
// Case.getCurrentCaseThrows().notifyDataSourceDeleted(dataSourceID);
// } catch (NoCurrentCaseException | TskCoreException | KeywordSearchServiceException e) {
// logger.log(Level.SEVERE, String.format("Error Deleting data source (obj_id=%d)", dataSourceID), e);
// }
}
@Override
public DeleteDataSourceAction clone() throws CloneNotSupportedException {
DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone());
clonedObject.setDataSourceID(this.dataSourceID);
return clonedObject;
}
private void setDataSourceID(long dataSourceID) {
this.dataSourceID = dataSourceID;
}
}

View File

@ -10,7 +10,6 @@ Case.deleteCaseConfirmationDialog.title=Delete Current Case?
Case.deleteCaseFailureMessageBox.message=Error deleting case: {0}
Case.deleteCaseFailureMessageBox.title=Failed to Delete Case
Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.
Case.ErrorDeletingDataSource.name.text=Error Deleting Data Source
Case.exceptionMessage.cancelledByUser=Cancelled by user.
Case.exceptionMessage.cannotDeleteCurrentCase=Cannot delete current case, it must be closed first.
Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is open for another user or host.
@ -38,6 +37,8 @@ Case.exceptionMessage.couldNotUpdateCaseNodeData=Failed to update coordination s
Case.exceptionMessage.deletionInterrupted=Deletion of the case {0} was cancelled.
Case.exceptionMessage.emptyCaseDir=Must specify a case directory path.
Case.exceptionMessage.emptyCaseName=Must specify a case name.
# {0} - exception message
Case.exceptionMessage.errorDeletingDataSource=An error occurred while deleting the data source:\n{0}.
Case.exceptionMessage.errorsDeletingCase=Errors occured while deleting the case. See the application log for details.
# {0} - exception message
Case.exceptionMessage.execExceptionWrapperMessage={0}
@ -54,14 +55,11 @@ Case.exceptionMessage.metadataUpdateError=Failed to update case metadata
Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.
Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User.
Case.progressIndicatorCancelButton.label=Cancel
Case.progressIndicatorStatus_closingCase=Closing Case to Deleting Data Source.
Case.progressIndicatorStatus_deletingDataSource=Deleting Data Source.
Case.progressIndicatorStatus_openingCase=Opening Case to Deleting Data Source.
Case.progressIndicatorTitle.closingCase=Closing Case
Case.progressIndicatorTitle.creatingCase=Creating Case
Case.progressIndicatorTitle.deletingCase=Deleting Case
Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source
Case.progressIndicatorTitle.openingCase=Opening Case
Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case.
Case.progressMessage.cancelling=Cancelling...
Case.progressMessage.clearingTempDirectory=Clearing case temp directory...
Case.progressMessage.closingApplicationServiceResources=Closing case-specific application service resources...
@ -124,6 +122,10 @@ CTL_CaseDetailsAction=Case Details
CTL_CaseDeleteAction=Delete Case
CTL_CaseOpenAction=Open Case
CTL_UnpackagePortableCaseAction=Unpack and Open Portable Case
DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?
DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to reopen the case.
DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.
DeleteDataSourceAction.name.text=Delete Data Source
EditOptionalCasePropertiesPanel.cancelButton.text=Cancel
EditOptionalCasePropertiesPanel.saveButton.text=Save
GeneralFilter.encaseImageDesc.text=Encase Images (*.e01)

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.casemodule;
import org.sleuthkit.autopsy.access.AccessLimiterUtils;
import com.google.common.annotations.Beta;
import com.google.common.eventbus.Subscribe;
import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData;
@ -52,6 +53,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -741,9 +743,9 @@ public class Case {
}
/**
* Deletes the selected data source.
* Deletes a data source from the current cases.
*
* @param dataSourceId id of the data source to delete.
* @param dataSourceObjectID The object ID of the data source to delete.
*
* @throws CaseActionException If there is a problem deleting the case. The
* exception will have a user-friendly message
@ -751,64 +753,20 @@ public class Case {
* exception.
*/
@Messages({
"Case.progressIndicatorTitle_deletingDataSource=Deleting Data Source from the case.",
"Case.progressIndicatorStatus_closingCase=Closing Case to Deleting Data Source.",
"Case.progressIndicatorStatus_openingCase=Opening Case to Deleting Data Source.",
"Case.progressIndicatorStatus_deletingDataSource=Deleting Data Source.",})
public static void deleteDataSourceFromCurrentCase(Long dataSourceId) throws CaseActionException {
"Case.progressIndicatorTitle.deletingDataSource=Deleting Data Source"
})
public static void deleteDataSourceFromCurrentCase(Long dataSourceObjectID) throws CaseActionException {
synchronized (caseActionSerializationLock) {
if (null == currentCase) {
return;
}
CaseMetadata newMetadata = null;
CaseMetadata metadata = currentCase.getMetadata();
String caseDir = metadata.getFilePath().toString();
try {
newMetadata = new CaseMetadata(Paths.get(caseDir));
} catch (CaseMetadataException ex) {
logger.log(Level.WARNING, String.format("Error Getting Case Dir %s", caseDir), ex);
}
ProgressIndicator progressIndicator;
if (RuntimeProperties.runningWithGUI()) {
progressIndicator = new ModalDialogProgressIndicator(mainFrame, Bundle.Case_progressIndicatorTitle_deletingDataSource());
} else {
progressIndicator = new LoggingProgressIndicator();
}
Case theCase = currentCase;
closeCurrentCase();
progressIndicator.switchToIndeterminate(Bundle.Case_progressIndicatorStatus_openingCase());
progressIndicator.start(Bundle.Case_progressIndicatorStatus_deletingDataSource());
deleteDataSource(dataSourceId, progressIndicator, metadata);
progressIndicator.finish();
openAsCurrentCase(new Case(newMetadata), false);
theCase.doCaseAction(Bundle.Case_progressIndicatorTitle_deletingDataSource(), null, CaseLockType.EXCLUSIVE, false, dataSourceObjectID);
openAsCurrentCase(theCase, false);
}
}
/**
* Delete a data source from the current case.
*
* @param dataSourceId id of the data source to delete.
* @param progressIndicator
*/
@Messages({
"Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.",
"Case.ErrorDeletingDataSource.name.text=Error Deleting Data Source"
})
static void deleteDataSource(Long dataSourceId, ProgressIndicator progressIndicator, CaseMetadata metadata) throws CaseActionException {
// get case actions lock
closeCurrentCase();
// If multi-user case, get exclusive lock on case, else continue w/o lock
Case theCase = new Case(metadata);
theCase.openCaseDataBase(progressIndicator);
theCase.openAppServiceCaseResources(progressIndicator);
try {
SleuthkitCaseAdmin.deleteDataSource(null, dataSourceId);
} catch (TskCoreException ex) {
}
eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId));
theCase.close(progressIndicator);
openAsCurrentCase(new Case(metadata), false);
}
/**
* Deletes a case. The case to be deleted must not be the "current case."
* Deleting the current case must be done by calling Case.deleteCurrentCase.
@ -870,6 +828,8 @@ public class Case {
* @throws CaseActionCancelledException If creating the case is cancelled.
*/
@Messages({
"Case.progressIndicatorTitle.creatingCase=Creating Case",
"Case.progressIndicatorTitle.openingCase=Opening Case",
"Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window"
})
private static void openAsCurrentCase(Case newCurrentCase, boolean isNewCase) throws CaseActionException, CaseActionCancelledException {
@ -887,7 +847,16 @@ public class Case {
}
try {
logger.log(Level.INFO, "Opening {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
newCurrentCase.open(isNewCase);
String progressIndicatorTitle;
CaseAction<ProgressIndicator, Object, Void> caseAction;
if (isNewCase) {
progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_creatingCase();
caseAction = newCurrentCase::createCase;
} else {
progressIndicatorTitle = Bundle.Case_progressIndicatorTitle_openingCase();
caseAction = newCurrentCase::openCase;
}
newCurrentCase.doCaseAction(progressIndicatorTitle, caseAction, CaseLockType.SHARED, true, null);
currentCase = newCurrentCase;
logger.log(Level.INFO, "Opened {0} ({1}) in {2} as the current case", new Object[]{newCurrentCase.getDisplayName(), newCurrentCase.getName(), newCurrentCase.getCaseDirectory()}); //NON-NLS
if (RuntimeProperties.runningWithGUI()) {
@ -1553,18 +1522,6 @@ public class Case {
eventPublisher.publish(new DataSourceNameChangedEvent(dataSource, newName));
}
/**
* Notifies case event subscribers that a data source has been deleted from
* the case database.
*
* This should not be called from the event dispatch thread (EDT)
*
* @param dataSourceId The object ID of the data source that was deleted.
*/
public void notifyDataSourceDeleted(Long dataSourceId) {
eventPublisher.publish(new DataSourceDeletedEvent(dataSourceId));
}
/**
* Notifies case event subscribers that a content tag has been added.
*
@ -1795,23 +1752,30 @@ public class Case {
}
/**
* Opens this case by creating a task running in the same non-UI thread that
* will be used to close the case. If the case is a single-user case, this
* supports cancelling creation of the case by cancelling the task. If the
* case is a multi-user case, this ensures ensures that case directory lock
* held as long as the case is open is released in the same thread in which
* it was acquired, as is required by the coordination service.
* Performs a case action by creating a task running in the same non-UI
* thread that will be used to close the case. For both single-user and
* mulit-user cases, this supports cancelling the case action by cancelling
* the task. If the case is a multi-user case, this also ensures that the
* case lock is released in the same thread in which it was acquired, which
* is required by the coordination service.
*
* @param isNewCase True for a new case, false otherwise.
* @param progressIndicatorTitle A title for the progress indicator for the
* case action.
* @param caseAction The case action method.
* @param caseLockType The type of case lock required for the case
* action.
* @param allowCancellation Whether or not to allow the action to be
* cancelled.
* @param additionalParams An Object that holds any additional
* parameters for a case action. For this
* action, this is null.
*
* @throws CaseActionException If there is a problem creating the case. The
* exception will have a user-friendly message
* and may be a wrapper for a lower-level
* exception.
* @throws CaseActionException If there is a problem completing the action.
* The exception will have a user-friendly
* message and may be a wrapper for a
* lower-level exception.
*/
@Messages({
"Case.progressIndicatorTitle.creatingCase=Creating Case",
"Case.progressIndicatorTitle.openingCase=Opening Case",
"Case.progressIndicatorCancelButton.label=Cancel",
"Case.progressMessage.preparing=Preparing...",
"Case.progressMessage.preparingToOpenCaseResources=<html>Preparing to open case resources.<br>This may take time if another user is upgrading the case.</html>",
@ -1819,62 +1783,66 @@ public class Case {
"Case.exceptionMessage.cancelledByUser=Cancelled by user.",
"# {0} - exception message", "Case.exceptionMessage.execExceptionWrapperMessage={0}"
})
private void open(boolean isNewCase) throws CaseActionException {
private void doCaseAction(String progressIndicatorTitle, CaseAction<ProgressIndicator, Object, Void> caseAction, CaseLockType caseLockType, boolean allowCancellation, Object additionalParams) throws CaseActionException {
/*
* Create and start either a GUI progress indicator with a Cancel button
* or a logging progress indicator.
* Create and start either a GUI progress indicator or a logging
* progress indicator.
*/
CancelButtonListener cancelButtonListener = null;
ProgressIndicator progressIndicator;
if (RuntimeProperties.runningWithGUI()) {
cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
String progressIndicatorTitle = isNewCase ? Bundle.Case_progressIndicatorTitle_creatingCase() : Bundle.Case_progressIndicatorTitle_openingCase();
progressIndicator = new ModalDialogProgressIndicator(
mainFrame,
progressIndicatorTitle,
new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
Bundle.Case_progressIndicatorCancelButton_label(),
cancelButtonListener);
if (allowCancellation) {
cancelButtonListener = new CancelButtonListener(Bundle.Case_progressMessage_cancelling());
progressIndicator = new ModalDialogProgressIndicator(
mainFrame,
progressIndicatorTitle,
new String[]{Bundle.Case_progressIndicatorCancelButton_label()},
Bundle.Case_progressIndicatorCancelButton_label(),
cancelButtonListener);
} else {
progressIndicator = new ModalDialogProgressIndicator(
mainFrame,
progressIndicatorTitle);
}
} else {
progressIndicator = new LoggingProgressIndicator();
}
progressIndicator.start(Bundle.Case_progressMessage_preparing());
/*
* Creating/opening a case is always done by creating a task running in
* the same non-UI thread that will be used to close the case, so a
* A case action is always done by creating a task running in the same
* non-UI thread that will be used to close the case, so a
* single-threaded executor service is created here and saved as case
* state (must be volatile for cancellation to work).
*
* --- If the case is a single-user case, this supports cancelling
* opening of the case by cancelling the task.
* --- If the case is a single-user case, this supports cancelling the
* case action by cancelling the task.
*
* --- If the case is a multi-user case, this still supports
* cancellation, but it also makes it possible for the shared case
* directory lock held as long as the case is open to be released in the
* same thread in which it was acquired, as is required by the
* coordination service.
* cancellation, but it also makes it possible for the case lock held as
* long as the case is open to be released in the same thread in which
* it was acquired, as is required by the coordination service.
*/
TaskThreadFactory threadFactory = new TaskThreadFactory(String.format(CASE_ACTION_THREAD_NAME, metadata.getCaseName()));
caseLockingExecutor = Executors.newSingleThreadExecutor(threadFactory);
Future<Void> future = caseLockingExecutor.submit(() -> {
if (CaseType.SINGLE_USER_CASE == metadata.getCaseType()) {
open(isNewCase, progressIndicator);
caseAction.execute(progressIndicator, additionalParams);
} else {
/*
* First, acquire a shared case directory lock that will be held
* as long as this node has this case open. This will prevent
* deletion of the case by another node. Next, acquire an
* exclusive case resources lock to ensure only one node at a
* time can create/open/upgrade/close the case resources.
* First, acquire a case lock that will be held as long as this
* node has this case open. This will prevent deletion of the
* case by another node. Next, acquire an exclusive case
* resources lock to ensure only one node at a time can
* create/open/upgrade/close the case resources.
*/
progressIndicator.progress(Bundle.Case_progressMessage_preparingToOpenCaseResources());
acquireSharedCaseDirLock(metadata.getCaseDirectory());
acquireCaseLock(caseLockType, metadata.getCaseDirectory());
try (CoordinationService.Lock resourcesLock = acquireExclusiveCaseResourcesLock(metadata.getCaseDirectory())) {
if (null == resourcesLock) {
throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireResourcesLock());
}
open(isNewCase, progressIndicator);
caseAction.execute(progressIndicator, additionalParams);
} catch (CaseActionException ex) {
releaseSharedCaseDirLock(getMetadata().getCaseDirectory());
throw ex;
@ -1887,18 +1855,17 @@ public class Case {
}
/*
* Wait for the case creation/opening task to finish.
* Wait for the case action task to finish.
*/
try {
future.get();
} catch (InterruptedException discarded) {
/*
* The thread this method is running in has been interrupted. Cancel
* the create/open task, wait for it to finish, and shut down the
* the case action task, wait for it to finish, and shut down the
* executor. This can be done safely because if the task is
* completed with a cancellation condition, the case will have been
* closed and the case directory lock released will have been
* released.
* closed and the case lock will have been released.
*/
if (null != cancelButtonListener) {
cancelButtonListener.actionPerformed(null);
@ -1908,21 +1875,20 @@ public class Case {
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
} catch (CancellationException discarded) {
/*
* The create/open task has been cancelled. Wait for it to finish,
* The case action task has been cancelled. Wait for it to finish,
* and shut down the executor. This can be done safely because if
* the task is completed with a cancellation condition, the case
* will have been closed and the case directory lock released will
* have been released.
* will have been closed and the case lock will have been released.
*/
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
throw new CaseActionCancelledException(Bundle.Case_exceptionMessage_cancelledByUser());
} catch (ExecutionException ex) {
/*
* The create/open task has thrown an exception. Wait for it to
* The case action task has thrown an exception. Wait for it to
* finish, and shut down the executor. This can be done safely
* because if the task is completed with an execution condition, the
* case will have been closed and the case directory lock released
* will have been released.
* case will have been closed and the case lock will have been
* released.
*/
ThreadUtils.shutDownTaskExecutor(caseLockingExecutor);
throw new CaseActionException(Bundle.Case_exceptionMessage_execExceptionWrapperMessage(ex.getCause().getLocalizedMessage()), ex);
@ -1932,48 +1898,41 @@ public class Case {
}
/**
* Opens the case database and services for this case.
* A case action (interface CaseAction<T, V, R>) that creates the case
* directory and case database and opens the application services for this
* case.
*
* @param isNewCase True for a new case, false otherwise.
* @param progressIndicator A progress indicator.
* @param additionalParams An Object that holds any additional parameters
* for a case action. For this action, this is
* null.
*
* @throws CaseActionException If there is a problem creating the case. The
* exception will have a user-friendly message
* and may be a wrapper for a lower-level
* exception.
* @throws CaseActionException If there is a problem completing the action.
* The exception will have a user-friendly
* message and may be a wrapper for a
* lower-level exception.
*/
private void open(boolean isNewCase, ProgressIndicator progressIndicator) throws CaseActionException {
private Void createCase(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
assert (additionalParams == null);
try {
checkForUserCancellation();
createCaseDirectoryIfDoesNotExist(progressIndicator);
checkForUserCancellation();
switchLoggingToCaseLogsDirectory(progressIndicator);
checkForUserCancellation();
if (isNewCase) {
saveCaseMetadataToFile(progressIndicator);
}
saveCaseMetadataToFile(progressIndicator);
checkForUserCancellation();
if (isNewCase) {
createCaseNodeData(progressIndicator);
} else {
updateCaseNodeData(progressIndicator);
}
createCaseNodeData(progressIndicator);
checkForUserCancellation();
if (!isNewCase) {
deleteTempfilesFromCaseDirectory(progressIndicator);
}
checkForUserCancellation();
if (isNewCase) {
createCaseDatabase(progressIndicator);
} else {
openCaseDataBase(progressIndicator);
}
createCaseDatabase(progressIndicator);
checkForUserCancellation();
openCaseLevelServices(progressIndicator);
checkForUserCancellation();
openAppServiceCaseResources(progressIndicator);
checkForUserCancellation();
openCommunicationChannels(progressIndicator);
return null;
} catch (CaseActionException ex) {
/*
@ -1991,6 +1950,96 @@ public class Case {
}
}
/**
* A case action (interface CaseAction<T, V, R>) that opens the case
* database and services for this case.
*
* @param progressIndicator A progress indicator.
* @param additionalParams An Object that holds any additional parameters
* for a case action. For this action, this is
* null.
*
* @throws CaseActionException If there is a problem completing the action.
* The exception will have a user-friendly
* message and may be a wrapper for a
* lower-level exception.
*/
private Void openCase(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
assert (additionalParams == null);
try {
checkForUserCancellation();
switchLoggingToCaseLogsDirectory(progressIndicator);
checkForUserCancellation();
updateCaseNodeData(progressIndicator);
checkForUserCancellation();
deleteTempfilesFromCaseDirectory(progressIndicator);
checkForUserCancellation();
openCaseDataBase(progressIndicator);
checkForUserCancellation();
openCaseLevelServices(progressIndicator);
checkForUserCancellation();
openAppServiceCaseResources(progressIndicator);
checkForUserCancellation();
openCommunicationChannels(progressIndicator);
return null;
} catch (CaseActionException ex) {
/*
* Cancellation or failure. Clean up by calling the close method.
* The sleep is a little hack to clear the interrupted flag for this
* thread if this is a cancellation scenario, so that the clean up
* can run to completion in the current thread.
*/
try {
Thread.sleep(1);
} catch (InterruptedException discarded) {
}
close(progressIndicator);
throw ex;
}
}
/**
* A case action (interface CaseAction<T, V, R>) for a closed case that
* opens the case database and application services for this case, deletes a
* data source from the case, and publishes an application event indicasting
* the data source has been deleted.
*
* Note that this case action does not support cancellation.
*
* @param progressIndicator A progress indicator.
* @param additionalParams An Object that holds any additional parameters
* for a case action. For this action, this the
* object ID of the data source to be deleted.
*
* @throws CaseActionException If there is a problem completing the action.
* The exception will have a user-friendly
* message and may be a wrapper for a
* lower-level exception.
*/
@Messages({
"Case.DeletingDataSourceFromCase=Deleting the Data Source from the case.",
"# {0} - exception message", "Case.exceptionMessage.errorDeletingDataSource=An error occurred while deleting the data source:\n{0}."
})
Void deleteDataSource(ProgressIndicator progressIndicator, Object additionalParams) throws CaseActionException {
assert (additionalParams instanceof Long);
openCaseDataBase(progressIndicator);
openAppServiceCaseResources(progressIndicator);
Long dataSourceObjectID = (Long) additionalParams;
try {
SleuthkitCaseAdmin.deleteDataSource(this.caseDb, dataSourceObjectID);
} catch (TskCoreException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex);
}
try {
this.caseServices.getKeywordSearchService().deleteDataSource(dataSourceObjectID);
} catch (KeywordSearchServiceException ex) {
throw new CaseActionException(Bundle.Case_exceptionMessage_errorDeletingDataSource(ex.getMessage()), ex);
}
eventPublisher.publish(new DataSourceDeletedEvent(dataSourceObjectID));
return null;
}
/**
* Create an empty portable case from the current case
*
@ -2475,7 +2524,7 @@ public class Case {
/*
* The wait has been interrupted by interrupting the thread running
* this method. Not allowing cancellation of case closing, so ignore
* the interrupt. Likewsie, cancellation of the case closing task is
* the interrupt. Likewise, cancellation of the case closing task is
* not supported.
*/
} catch (ExecutionException ex) {
@ -2595,9 +2644,13 @@ public class Case {
* cannot be acquired.
*/
@Messages({"Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory"})
private void acquireSharedCaseDirLock(String caseDir) throws CaseActionException {
private void acquireCaseLock(CaseLockType lockType, String caseDir) throws CaseActionException {
try {
caseDirLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
boolean flag = true;
CoordinationService coordinationService = CoordinationService.getInstance();
caseDirLock = lockType == CaseLockType.SHARED
? coordinationService.tryGetSharedLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS)
: coordinationService.tryGetExclusiveLock(CategoryNode.CASES, caseDir, DIR_LOCK_TIMOUT_HOURS, TimeUnit.HOURS);
if (null == caseDirLock) {
throw new CaseActionException(Bundle.Case_creationException_couldNotAcquireDirLock());
}
@ -2994,6 +3047,15 @@ public class Case {
}
}
private interface CaseAction<T, V, R> {
R execute(T t, V v) throws CaseActionException;
}
private enum CaseLockType {
SHARED, EXCLUSIVE;
}
/**
* A case operation Cancel button listener for use with a
* ModalDialogProgressIndicator when running with a GUI.

View File

@ -0,0 +1,107 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.casemodule;
import java.awt.event.ActionEvent;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.SwingWorker;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
/**
* An Action that allows a user to delete a data source from the current case.
*/
public final class DeleteDataSourceAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(DeleteDataSourceAction.class.getName());
private long dataSourceObjectID;
private Path caseMetadataFilePath;
/**
* Constructs an Action that allows a user to delete a data source.
*
* @param dataSourceObjectID The object ID of the data source to be deleted.
*/
@NbBundle.Messages({
"DeleteDataSourceAction.name.text=Delete Data Source"
})
public DeleteDataSourceAction(Long dataSourceObjectID) {
super(Bundle.DeleteDataSourceAction_name_text());
this.dataSourceObjectID = dataSourceObjectID;
}
@NbBundle.Messages({
"DeleteDataSourceAction.confirmationDialog.message=Are you sure you want to delete the selected data source from the case?",
"DeleteDataSourceAction.exceptionMessage.dataSourceDeletionError=An error occurred while deleting the data source.\nPlease see the application log for details.",
"DeleteDataSourceAction.exceptionMessage.couldNotReopenCase=Failed to reopen the case.",})
@Override
public void actionPerformed(ActionEvent event) {
if (MessageNotifyUtil.Message.confirm(Bundle.DeleteDataSourceAction_confirmationDialog_message())) {
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
caseMetadataFilePath = Case.getCurrentCase().getMetadata().getFilePath();
/*
* Note that the case is closed and re-opened by this case
* action.
*/
Case.deleteDataSourceFromCurrentCase(dataSourceObjectID);
return null;
}
@Override
protected void done() {
try {
get();
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, String.format("Error deleting data source (obj_id=%d)", dataSourceObjectID), ex);
MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_dataSourceDeletionError(), MessageNotifyUtil.MessageType.ERROR);
if (!Case.isCaseOpen()) {
try {
Case.openAsCurrentCase(caseMetadataFilePath.toString());
} catch (CaseActionException ex2) {
logger.log(Level.SEVERE, "Failed to reopen the case after data source deletion error", ex2);
MessageNotifyUtil.Message.show(Bundle.DeleteDataSourceAction_exceptionMessage_couldNotReopenCase(), MessageNotifyUtil.MessageType.ERROR);
StartupWindowProvider.getInstance().open();
}
}
}
}
}.execute();
}
}
@Override
public DeleteDataSourceAction clone() throws CloneNotSupportedException {
DeleteDataSourceAction clonedObject = ((DeleteDataSourceAction) super.clone());
clonedObject.setDataSourceID(this.dataSourceObjectID);
return clonedObject;
}
private void setDataSourceID(long dataSourceID) {
this.dataSourceObjectID = dataSourceID;
}
}

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.casemodule;
import org.sleuthkit.autopsy.access.AccessLimiterUtils;
import java.awt.Component;
import org.openide.util.NbBundle;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2019 Basis Technology Corp.
* Copyright 2012-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -20,8 +20,6 @@ package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.nio.file.Paths;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
@ -35,14 +33,14 @@ import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.actions.DeleteDataSourceAction;
import org.sleuthkit.autopsy.access.AccessLimiterUtils;
import org.sleuthkit.autopsy.casemodule.DeleteDataSourceAction;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
@ -66,10 +64,6 @@ public class ImageNode extends AbstractContentNode<Image> {
private static final Logger logger = Logger.getLogger(ImageNode.class.getName());
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.CONTENT_CHANGED);
private final static String ADMIN_ACCESS_FILE_NAME = "admin"; // NON-NLS
private final static String ADMIN_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), ADMIN_ACCESS_FILE_NAME).toString();
private final static String ADMIN_EXT_ACCESS_FILE_NAME = "adminext"; // NON-NLS
private final static String ADMIN_EXT_ACCESS_FILE_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), ADMIN_EXT_ACCESS_FILE_NAME).toString();
/**
* Helper so that the display name and the name used in building the path
@ -128,7 +122,7 @@ public class ImageNode extends AbstractContentNode<Image> {
actionsList.add(new RunIngestModulesAction(Collections.<Content>singletonList(content)));
actionsList.add(new NewWindowViewAction(
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this));
if (checkSchemaVersion() && checkMuAdmin()) {
if (canAddDeleteDataSourceAction()) {
actionsList.add(new DeleteDataSourceAction(content.getId()));
}
return actionsList.toArray(new Action[0]);
@ -218,31 +212,19 @@ public class ImageNode extends AbstractContentNode<Image> {
return getClass().getName();
}
private Boolean checkSchemaVersion() {
try {
CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCaseThrows().getSleuthkitCase().getDBSchemaCreationVersion();
if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || creationVersion.getMajor() > 8) {
return true;
}
} catch (NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Failed to get creation schema version: ", ex);
}
return false;
}
private Boolean checkMuAdmin() {
try {
if (Case.CaseType.MULTI_USER_CASE == Case.getCurrentCaseThrows().getCaseType()) {
return new File(ADMIN_ACCESS_FILE_PATH).exists() || new File(ADMIN_EXT_ACCESS_FILE_PATH).exists();
}
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Failed to get the Create Major and Minor Schema Versions", ex);
/**
* Determines whether or not the delete data source action can be added.
* @return True or false.
*/
private Boolean canAddDeleteDataSourceAction() {
boolean canAddAction = false;
CaseDbSchemaVersionNumber creationVersion = Case.getCurrentCase().getSleuthkitCase().getDBSchemaCreationVersion();
if ((creationVersion.getMajor() == 8 && creationVersion.getMinor() >= 3) || (creationVersion.getMajor() > 8)) {
canAddAction = Case.getCurrentCase().getCaseType() == Case.CaseType.SINGLE_USER_CASE || !AccessLimiterUtils.limitMultiUserAccess();
}
return true;
return canAddAction;
}
/*
* This property change listener refreshes the tree when a new file is
* carved out of this image (i.e, the image is being treated as raw bytes