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,210 @@
|
||||
/*
|
||||
* 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 java.util.Collection;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
|
||||
import org.alfresco.repo.node.NodeServicePolicies;
|
||||
import org.alfresco.repo.policy.annotation.Behaviour;
|
||||
import org.alfresco.repo.policy.annotation.BehaviourBean;
|
||||
import org.alfresco.repo.policy.annotation.BehaviourKind;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
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.cmr.repository.NodeService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Content destruction component.
|
||||
* <p>
|
||||
* Listens for the destruction of sensitive nodes (classified content and records) and schedules
|
||||
* all their content for immediate destruction.
|
||||
* <p>
|
||||
* If enabled, the content is also cleansed before destruction.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 3.0.a
|
||||
*/
|
||||
@BehaviourBean
|
||||
public class ContentDestructionComponent implements NodeServicePolicies.BeforeDeleteNodePolicy
|
||||
{
|
||||
/** authentication utils */
|
||||
private AuthenticationUtil authenticationUtil;
|
||||
|
||||
/** content classification service */
|
||||
private ContentClassificationService contentClassificationService;
|
||||
|
||||
/** record service */
|
||||
private RecordService recordService;
|
||||
|
||||
/** eager content store cleaner */
|
||||
private EagerContentStoreCleaner eagerContentStoreCleaner;
|
||||
|
||||
/** dictionary service */
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
/** node service */
|
||||
private NodeService nodeService;
|
||||
|
||||
/** indicates whether cleansing is enabled or not */
|
||||
private boolean cleansingEnabled = false;
|
||||
|
||||
/**
|
||||
* @param authenticationUtil authentication utils
|
||||
*/
|
||||
public void setAuthenticationUtil(AuthenticationUtil authenticationUtil)
|
||||
{
|
||||
this.authenticationUtil = authenticationUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentClassificationService content classification service
|
||||
*/
|
||||
public void setContentClassificationService(ContentClassificationService contentClassificationService)
|
||||
{
|
||||
this.contentClassificationService = contentClassificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recordService record service
|
||||
*/
|
||||
public void setRecordService(RecordService recordService)
|
||||
{
|
||||
this.recordService = recordService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param eagerContentStoreCleaner eager content store cleaner
|
||||
*/
|
||||
public void setEagerContentStoreCleaner(EagerContentStoreCleaner eagerContentStoreCleaner)
|
||||
{
|
||||
this.eagerContentStoreCleaner = eagerContentStoreCleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dictionaryService dictionary service
|
||||
*/
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeService node service
|
||||
*/
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cleansingEnabled true if cleansing enabled, false otherwise
|
||||
*/
|
||||
public void setCleansingEnabled(boolean cleansingEnabled)
|
||||
{
|
||||
this.cleansingEnabled = cleansingEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if cleansing is enabled, false otherwise
|
||||
*/
|
||||
public boolean isCleansingEnabled()
|
||||
{
|
||||
return cleansingEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* System behaviour implementation that listens for sensitive nodes
|
||||
* and schedules them for immediate destruction.
|
||||
* <p>
|
||||
* Note that the content destruction and cleansing takes place on transaction
|
||||
* commit. If the transaction is rolled back after this behaviour is encountered
|
||||
* then the content will not be destroyed or cleansed.
|
||||
*
|
||||
* @param nodeRef node reference about to be deleted
|
||||
*/
|
||||
@Override
|
||||
@Behaviour
|
||||
(
|
||||
isService = true,
|
||||
kind = BehaviourKind.CLASS
|
||||
)
|
||||
public void beforeDeleteNode(final NodeRef nodeRef)
|
||||
{
|
||||
authenticationUtil.runAsSystem(new RunAsWork<Void>()
|
||||
{
|
||||
public Void doWork() throws Exception
|
||||
{
|
||||
// if enable and content is classified or a record
|
||||
if (contentClassificationService.isClassified(nodeRef) ||
|
||||
recordService.isRecord(nodeRef))
|
||||
{
|
||||
// then register all content for destruction
|
||||
registerAllContentForDestruction(nodeRef);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all content on the given node for destruction.
|
||||
*
|
||||
* @param nodeRef node reference
|
||||
*/
|
||||
private void registerAllContentForDestruction(NodeRef nodeRef)
|
||||
{
|
||||
// get node type
|
||||
QName nodeType = nodeService.getType(nodeRef);
|
||||
|
||||
// get type properties
|
||||
Collection<QName> nodeProperties = dictionaryService.getAllProperties(nodeType);
|
||||
for (QName nodeProperty : nodeProperties)
|
||||
{
|
||||
// get property definition
|
||||
PropertyDefinition propertyDefinition = dictionaryService.getProperty(nodeProperty);
|
||||
|
||||
// if content property
|
||||
if (propertyDefinition != null &&
|
||||
DataTypeDefinition.CONTENT.equals(propertyDefinition.getDataType().getName()))
|
||||
{
|
||||
// get content data
|
||||
ContentData dataContent = (ContentData)nodeService.getProperty(nodeRef, nodeProperty);
|
||||
|
||||
// if enabled cleanse content
|
||||
if (isCleansingEnabled())
|
||||
{
|
||||
// register for cleanse then immediate destruction
|
||||
eagerContentStoreCleaner.registerOrphanedContentUrlForCleansing(dataContent.getContentUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
// register for immediate destruction
|
||||
eagerContentStoreCleaner.registerOrphanedContentUrl(dataContent.getContentUrl(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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 java.io.File;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.TransactionalResourceHelper;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Eager content store cleaner that allows content to be registered for cleansing before
|
||||
* destruction.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 3.0.a
|
||||
*/
|
||||
public class EagerContentStoreCleaner extends org.alfresco.repo.content.cleanup.EagerContentStoreCleaner
|
||||
{
|
||||
/** transaction resource key */
|
||||
protected static final String KEY_POST_COMMIT_CLEANSING_URLS = "postCommitCleansingUrls";
|
||||
|
||||
/** logger */
|
||||
private static Log logger = LogFactory.getLog(EagerContentStoreCleaner.class);
|
||||
|
||||
/** transactional resource helper */
|
||||
private TransactionalResourceHelper transactionalResourceHelper;
|
||||
|
||||
/** content cleanser */
|
||||
private ContentCleanser contentCleanser;
|
||||
|
||||
/**
|
||||
* @param transactionResourceHelper transactional resource helper
|
||||
*/
|
||||
public void setTransactionalResourceHelper(TransactionalResourceHelper transactionalResourceHelper)
|
||||
{
|
||||
this.transactionalResourceHelper = transactionalResourceHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentCleanser content cleanser
|
||||
*/
|
||||
public void setContentCleanser(ContentCleanser contentCleanser)
|
||||
{
|
||||
this.contentCleanser = contentCleanser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers orphaned content URLs for cleansing
|
||||
*
|
||||
* @param contentUrl content url
|
||||
*/
|
||||
public void registerOrphanedContentUrlForCleansing(String contentUrl)
|
||||
{
|
||||
// make note of content that needs cleansing
|
||||
Set<String> cleansingUrls = transactionalResourceHelper.getSet(KEY_POST_COMMIT_CLEANSING_URLS);
|
||||
cleansingUrls.add(contentUrl);
|
||||
|
||||
// register as usual
|
||||
registerOrphanedContentUrl(contentUrl, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.content.cleanup.EagerContentStoreCleaner#deleteFromStore(java.lang.String, org.alfresco.repo.content.ContentStore)
|
||||
*/
|
||||
@Override
|
||||
protected boolean deleteFromStore(String contentUrl, ContentStore store)
|
||||
{
|
||||
// determine if the content requires cleansing or not
|
||||
Set<String> cleansingUrls = transactionalResourceHelper.getSet(KEY_POST_COMMIT_CLEANSING_URLS);
|
||||
if (cleansingUrls.contains(contentUrl))
|
||||
{
|
||||
// cleanse content before delete
|
||||
cleanseContent(contentUrl, store);
|
||||
}
|
||||
|
||||
// delete from store
|
||||
return super.deleteFromStore(contentUrl, store);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanse content
|
||||
*
|
||||
* @param contentUrl content url
|
||||
* @param store content store
|
||||
*/
|
||||
private void cleanseContent(String contentUrl, ContentStore store)
|
||||
{
|
||||
if (contentCleanser == null)
|
||||
{
|
||||
logger.error(
|
||||
"No content cleanser specified. Unable to cleanse: \n" +
|
||||
" URL: " + contentUrl + "\n" +
|
||||
" Source: " + store);
|
||||
}
|
||||
else
|
||||
{
|
||||
// First check if the content is present at all
|
||||
ContentReader reader = store.getReader(contentUrl);
|
||||
if (reader != null && reader.exists())
|
||||
{
|
||||
// Call to implementation's shred
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"About to cleanse: \n" +
|
||||
" URL: " + contentUrl + "\n" +
|
||||
" Source: " + store);
|
||||
}
|
||||
try
|
||||
{
|
||||
if (reader instanceof FileContentReader)
|
||||
{
|
||||
// get file content
|
||||
FileContentReader fileReader = (FileContentReader) reader;
|
||||
File file = fileReader.getFile();
|
||||
|
||||
// cleanse content
|
||||
contentCleanser.cleanse(file);
|
||||
}
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
logger.error(
|
||||
"Content cleansing failed: \n" +
|
||||
" URL: " + contentUrl + "\n" +
|
||||
" Source: " + store + "\n" +
|
||||
" Reader: " + reader,
|
||||
e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.error(
|
||||
"Content no longer exists. Unable to cleanse: \n" +
|
||||
" URL: " + contentUrl + "\n" +
|
||||
" Source: " + store);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Content cleanser base implementation.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 3.0.a
|
||||
*/
|
||||
public abstract class ContentCleanser
|
||||
{
|
||||
/**
|
||||
* Cleanse file
|
||||
*
|
||||
* @param file file to cleanse
|
||||
*/
|
||||
public abstract void cleanse(File file);
|
||||
|
||||
/**
|
||||
* Overwrite files bytes with provided overwrite operation
|
||||
*
|
||||
* @param file file
|
||||
* @param overwriteOperation overwrite operation
|
||||
*/
|
||||
protected void overwrite(File file, OverwriteOperation overwriteOperation)
|
||||
{
|
||||
// get the number of bytes
|
||||
long bytes = file.length();
|
||||
try
|
||||
{
|
||||
// get an output stream
|
||||
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < bytes; i++)
|
||||
{
|
||||
// overwrite byte
|
||||
overwriteOperation.operation(os);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// close ouput stream
|
||||
try {os.close(); } catch (Throwable e) {}
|
||||
}
|
||||
}
|
||||
catch (IOException ioException)
|
||||
{
|
||||
// re-throw
|
||||
throw new RuntimeException("Unable to overwrite file", ioException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite operation
|
||||
*/
|
||||
protected abstract class OverwriteOperation
|
||||
{
|
||||
public abstract void operation(OutputStream os) throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite with zeros operation
|
||||
*/
|
||||
protected OverwriteOperation overwriteZeros = new OverwriteOperation()
|
||||
{
|
||||
public void operation(OutputStream os) throws IOException
|
||||
{
|
||||
os.write(0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Overwrite with ones operation
|
||||
*/
|
||||
protected OverwriteOperation overwriteOnes = new OverwriteOperation()
|
||||
{
|
||||
public void operation(OutputStream os) throws IOException
|
||||
{
|
||||
os.write(1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Overwrite with random operation
|
||||
*/
|
||||
protected OverwriteOperation overwriteRandom = new OverwriteOperation()
|
||||
{
|
||||
private Random random = new Random();
|
||||
|
||||
public void operation(OutputStream os) throws IOException
|
||||
{
|
||||
byte[] randomByte = new byte[1];
|
||||
random.nextBytes(randomByte);
|
||||
os.write(randomByte[0]);
|
||||
}
|
||||
};
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 java.io.File;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
|
||||
/**
|
||||
* DoD 5220-22M data cleansing implementation.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
* @since 3.0.a
|
||||
*/
|
||||
public class ContentCleanser522022M extends ContentCleanser
|
||||
{
|
||||
/**
|
||||
* @see org.alfresco.module.org_alfresco_module_rm.content.cleanser.ContentCleanser#cleanse(java.io.File)
|
||||
*/
|
||||
@Override
|
||||
public void cleanse(File file)
|
||||
{
|
||||
// Double check
|
||||
if (!file.exists() || !file.canWrite())
|
||||
{
|
||||
throw new ContentIOException("Unable to write to file: " + file);
|
||||
}
|
||||
|
||||
// Overwite file
|
||||
overwrite(file, overwriteOnes);
|
||||
overwrite(file, overwriteZeros);
|
||||
overwrite(file, overwriteRandom);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user