mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Data destruction and cleansing
* added content destruction component which ensures all records and classified content are immediately destroyed and optionally cleansed * extension to eager content cleaner to allow cleansing to take place just before the content is deleted from the content store * base content cleanser * simple implementation of DoD 5220-22M cleansing algoritm * data cleansing enabled global configuration * data cleansing bean configuration * unit tests * see RM-2463 and RM-2464 +review RM git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@109121 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.content;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser;
|
||||
import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
|
||||
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
/**
|
||||
* Content destruction component unit test.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 3.0.a
|
||||
*/
|
||||
public class ContentDestructionComponentUnitTest extends BaseUnitTest
|
||||
{
|
||||
@InjectMocks private ContentDestructionComponent contentDestructionComponent;
|
||||
|
||||
@Mock private ContentClassificationService mockedContentClassificationService;
|
||||
@Mock private ContentCleanser mockedContentCleanser;
|
||||
@Mock private EagerContentStoreCleaner mockedEagerContentStoreCleaner;
|
||||
|
||||
/**
|
||||
* Given a non-sensitive node
|
||||
* When it is deleted
|
||||
* Then nothing happens
|
||||
*/
|
||||
@Test
|
||||
public void deleteNonSensitiveNode()
|
||||
{
|
||||
NodeRef nodeRef = generateCmContent("myContent.txt");
|
||||
|
||||
when(mockedRecordService.isRecord(nodeRef))
|
||||
.thenReturn(false);
|
||||
when(mockedContentClassificationService.isClassified(nodeRef))
|
||||
.thenReturn(false);
|
||||
|
||||
contentDestructionComponent.beforeDeleteNode(nodeRef);
|
||||
|
||||
verifyZeroInteractions(mockedEagerContentStoreCleaner, mockedDictionaryService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a record
|
||||
* When it is deleted
|
||||
* Then it is sent for immediate destruction
|
||||
*/
|
||||
@Test
|
||||
public void deleteRecord()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
NodeRef nodeRef = generateDeletedNodeRef(contentURL);
|
||||
|
||||
when(mockedRecordService.isRecord(nodeRef))
|
||||
.thenReturn(true);
|
||||
when(mockedContentClassificationService.isClassified(nodeRef))
|
||||
.thenReturn(false);
|
||||
|
||||
contentDestructionComponent.beforeDeleteNode(nodeRef);
|
||||
|
||||
verify(mockedEagerContentStoreCleaner).registerOrphanedContentUrl(contentURL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given classified content
|
||||
* When it is deleted
|
||||
* Then it is send for immediate destruction
|
||||
*/
|
||||
@Test
|
||||
public void deleteClassifiedContent()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
NodeRef nodeRef = generateDeletedNodeRef(contentURL);
|
||||
|
||||
when(mockedRecordService.isRecord(nodeRef))
|
||||
.thenReturn(false);
|
||||
when(mockedContentClassificationService.isClassified(nodeRef))
|
||||
.thenReturn(true);
|
||||
|
||||
contentDestructionComponent.beforeDeleteNode(nodeRef);
|
||||
|
||||
verify(mockedEagerContentStoreCleaner).registerOrphanedContentUrl(contentURL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that content cleansing is turned on
|
||||
* When a sensitive node is deleted
|
||||
* Then it is not scheduled for cleansing before destruction
|
||||
*/
|
||||
@Test
|
||||
public void contentCleansingOn()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
NodeRef nodeRef = generateDeletedNodeRef(contentURL);
|
||||
|
||||
when(mockedRecordService.isRecord(nodeRef))
|
||||
.thenReturn(false);
|
||||
when(mockedContentClassificationService.isClassified(nodeRef))
|
||||
.thenReturn(true);
|
||||
|
||||
contentDestructionComponent.setCleansingEnabled(true);
|
||||
contentDestructionComponent.beforeDeleteNode(nodeRef);
|
||||
|
||||
verify(mockedEagerContentStoreCleaner).registerOrphanedContentUrlForCleansing(contentURL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that content cleansing is turned off
|
||||
* When a sensitive node is deleted
|
||||
* Then it is scheduled for cleansing before destruction
|
||||
*/
|
||||
@Test
|
||||
public void contentCleansingOff()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
NodeRef nodeRef = generateDeletedNodeRef(contentURL);
|
||||
|
||||
when(mockedRecordService.isRecord(nodeRef))
|
||||
.thenReturn(false);
|
||||
when(mockedContentClassificationService.isClassified(nodeRef))
|
||||
.thenReturn(true);
|
||||
|
||||
contentDestructionComponent.setCleansingEnabled(false);
|
||||
contentDestructionComponent.beforeDeleteNode(nodeRef);
|
||||
|
||||
verify(mockedEagerContentStoreCleaner).registerOrphanedContentUrl(contentURL, true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that a sensitive node has more than one content property
|
||||
* When is it deleted
|
||||
* Then all the content properties are scheduled for destruction
|
||||
*/
|
||||
@Test
|
||||
public void moreThanOneContentProperty()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
NodeRef nodeRef = generateDeletedNodeRef(contentURL, 2);
|
||||
|
||||
when(mockedRecordService.isRecord(nodeRef))
|
||||
.thenReturn(false);
|
||||
when(mockedContentClassificationService.isClassified(nodeRef))
|
||||
.thenReturn(true);
|
||||
|
||||
contentDestructionComponent.setCleansingEnabled(true);
|
||||
contentDestructionComponent.beforeDeleteNode(nodeRef);
|
||||
|
||||
verify(mockedEagerContentStoreCleaner, times(2)).registerOrphanedContentUrlForCleansing(contentURL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that creates deleted node reference
|
||||
*/
|
||||
private NodeRef generateDeletedNodeRef(String contentURL)
|
||||
{
|
||||
return generateDeletedNodeRef(contentURL, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that creates deleted node reference
|
||||
*/
|
||||
private NodeRef generateDeletedNodeRef(String contentURL, int contentPropertiesCount)
|
||||
{
|
||||
NodeRef nodeRef = generateCmContent("myContent.txt");
|
||||
|
||||
List<QName> contentProperties = new ArrayList<QName>(contentPropertiesCount);
|
||||
for (int i = 0; i < contentPropertiesCount; i++)
|
||||
{
|
||||
contentProperties.add(AlfMock.generateQName());
|
||||
}
|
||||
|
||||
when(mockedDictionaryService.getAllProperties(ContentModel.TYPE_CONTENT))
|
||||
.thenReturn(contentProperties);
|
||||
|
||||
DataTypeDefinition mockedDataTypeDefinition = mock(DataTypeDefinition.class);
|
||||
when(mockedDataTypeDefinition.getName())
|
||||
.thenReturn(DataTypeDefinition.CONTENT);
|
||||
|
||||
PropertyDefinition mockedPropertyDefinition = mock(PropertyDefinition.class);
|
||||
when(mockedPropertyDefinition.getDataType())
|
||||
.thenReturn(mockedDataTypeDefinition);
|
||||
|
||||
when(mockedDictionaryService.getProperty(any(QName.class)))
|
||||
.thenReturn(mockedPropertyDefinition);
|
||||
|
||||
ContentData mockedDataContent = mock(ContentData.class);
|
||||
when(mockedDataContent.getContentUrl())
|
||||
.thenReturn(contentURL);
|
||||
when(mockedNodeService.getProperty(eq(nodeRef), any(QName.class)))
|
||||
.thenReturn(mockedDataContent);
|
||||
|
||||
return nodeRef;
|
||||
}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.content;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser;
|
||||
import org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock;
|
||||
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
/**
|
||||
* Eager content store cleaner unit test.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 3.0.a
|
||||
*/
|
||||
public class EagerContentStoreCleanerUnitTest extends BaseUnitTest
|
||||
{
|
||||
@InjectMocks private EagerContentStoreCleaner eagerContentStoreCleaner = new EagerContentStoreCleaner()
|
||||
{
|
||||
/** dummy implementation */
|
||||
public boolean registerOrphanedContentUrl(String contentUrl, boolean force) {return true;};
|
||||
};
|
||||
|
||||
@Mock private ContentCleanser mockedContentCleanser;
|
||||
|
||||
/**
|
||||
* When content is registered for cleansing
|
||||
* Then the content URL is recorded for use later
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void registerContentURL()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
Set<Object> mockedSet = mock(Set.class);
|
||||
when(mockedTransactionalResourceHelper.getSet(EagerContentStoreCleaner.KEY_POST_COMMIT_CLEANSING_URLS))
|
||||
.thenReturn(mockedSet);
|
||||
|
||||
eagerContentStoreCleaner.registerOrphanedContentUrlForCleansing(contentURL);
|
||||
|
||||
verify(mockedSet).add(contentURL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the content requires cleansing
|
||||
* When the content is deleted from the store
|
||||
* Then the content is cleansed first
|
||||
*/
|
||||
@Test
|
||||
public void contentRequiresCleaning()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
Set<Object> mockedSet = new HashSet<Object>(Arrays.asList(contentURL));
|
||||
when(mockedTransactionalResourceHelper.getSet(EagerContentStoreCleaner.KEY_POST_COMMIT_CLEANSING_URLS))
|
||||
.thenReturn(mockedSet);
|
||||
|
||||
FileContentReader mockedReader = mock(FileContentReader.class);
|
||||
when(mockedReader.exists())
|
||||
.thenReturn(true);
|
||||
|
||||
File mockedFile = mock(File.class);
|
||||
when(mockedReader.getFile())
|
||||
.thenReturn(mockedFile);
|
||||
|
||||
ContentStore mockedContentStore = mock(ContentStore.class);
|
||||
when(mockedContentStore.getReader(contentURL))
|
||||
.thenReturn(mockedReader);
|
||||
|
||||
eagerContentStoreCleaner.deleteFromStore(contentURL, mockedContentStore);
|
||||
|
||||
verify(mockedContentCleanser).cleanse(mockedFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the content does not require cleansing
|
||||
* When the content is deleted from the store
|
||||
* Then the content is not cleansed
|
||||
*/
|
||||
@Test
|
||||
public void contentDoesntRequireCleaning()
|
||||
{
|
||||
String contentURL = AlfMock.generateText();
|
||||
Set<Object> mockedSet = new HashSet<Object>(Arrays.asList(contentURL));
|
||||
when(mockedTransactionalResourceHelper.getSet(EagerContentStoreCleaner.KEY_POST_COMMIT_CLEANSING_URLS))
|
||||
.thenReturn(mockedSet);
|
||||
|
||||
eagerContentStoreCleaner.deleteFromStore(AlfMock.generateText(), mock(ContentStore.class));
|
||||
|
||||
verifyZeroInteractions(mockedContentCleanser);
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.content.cleanser;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
|
||||
/**
|
||||
* Eager content store cleaner unit test.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 3.0.a
|
||||
*/
|
||||
public class ContentCleanser522022MUnitTest extends BaseUnitTest
|
||||
{
|
||||
@InjectMocks @Spy private ContentCleanser522022M contentCleanser522022M = new ContentCleanser522022M()
|
||||
{
|
||||
/** dummy implementations */
|
||||
protected void overwrite(File file, OverwriteOperation overwriteOperation) {};
|
||||
};
|
||||
|
||||
@Mock private File mockedFile;
|
||||
|
||||
/**
|
||||
* Given that a file exists
|
||||
* When I cleanse it
|
||||
* Then the content is overwritten
|
||||
*/
|
||||
@Test
|
||||
public void cleanseFile()
|
||||
{
|
||||
when(mockedFile.exists())
|
||||
.thenReturn(true);
|
||||
when(mockedFile.canWrite())
|
||||
.thenReturn(true);
|
||||
|
||||
contentCleanser522022M.cleanse(mockedFile);
|
||||
|
||||
verify(contentCleanser522022M)
|
||||
.overwrite(mockedFile, contentCleanser522022M.overwriteOnes);
|
||||
verify(contentCleanser522022M)
|
||||
.overwrite(mockedFile, contentCleanser522022M.overwriteZeros);
|
||||
verify(contentCleanser522022M)
|
||||
.overwrite(mockedFile, contentCleanser522022M.overwriteOnes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the file does not exist
|
||||
* When I cleanse it
|
||||
* Then an exception is thrown
|
||||
*/
|
||||
@Test
|
||||
(
|
||||
expected=ContentIOException.class
|
||||
)
|
||||
public void fileDoesNotExist()
|
||||
{
|
||||
when(mockedFile.exists())
|
||||
.thenReturn(false);
|
||||
when(mockedFile.canWrite())
|
||||
.thenReturn(true);
|
||||
|
||||
contentCleanser522022M.cleanse(mockedFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that I can not write to the file
|
||||
* When I cleanse it
|
||||
* Then an exception is thrown
|
||||
*/
|
||||
@Test
|
||||
(
|
||||
expected=ContentIOException.class
|
||||
)
|
||||
public void cantWriteToFile()
|
||||
{
|
||||
when(mockedFile.exists())
|
||||
.thenReturn(true);
|
||||
when(mockedFile.canWrite())
|
||||
.thenReturn(false);
|
||||
|
||||
contentCleanser522022M.cleanse(mockedFile);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user