mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
RM-2123 Create a new service for content classification.
Move methods to do with content from the ClassificationService and the SecurityClearanceService into the ContentClassificationService. Remove the dependency of SecurityClearanceService on ClassificationService which will allow us to reverse this dependency in the next commit. This is needed in order to filter classifications by the current user's clearance. Nb. This included adding a method in the SecurityClearanceService called isClearedForClassification, which looks quite similar to a new API Roy created hasClearance (see ContentClassificationService). In the future we should look to see if we can consolidate these. Remove dependency of ClassificationServiceBootstrap on the services, so that it can be passed into them. This allows us to provide access to the POJO managers in the services (this is made harder as the POJO managers aren't Spring beans). In order to initialise these objects, change the POJO managers to use setters rather than constructor arguments. This allows us to store a reference to the manager before the data has been loaded. Move the attribute service keys for classification levels and reasons into the ClassifiedContentModel. Expect NO_CLEARANCE to be passed into the ClearanceLevelManager, as otherwise we have to have logic to exclude it (see initialise in the old SecurityClearanceService) and then more logic to include it again (see the old constructor for ClearanceLevelManager). git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@104375 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -21,34 +21,33 @@ package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
|
||||
/**
|
||||
* Container for the configured {@link ClassificationLevel} objects.
|
||||
*
|
||||
*
|
||||
* @author tpage
|
||||
*/
|
||||
public class ClassificationLevelManager
|
||||
{
|
||||
/** Unclassified classificaiton level */
|
||||
public static final String UNCLASSIFIED_ID = "Unclassified";
|
||||
private static final String UNCLASSIFIED_MSG = "rm.classification.unclassified";
|
||||
public static final ClassificationLevel UNCLASSIFIED = new ClassificationLevel(UNCLASSIFIED_ID, UNCLASSIFIED_MSG);
|
||||
|
||||
/** Unclassified classification level */
|
||||
public static final String UNCLASSIFIED_ID = "Unclassified";
|
||||
private static final String UNCLASSIFIED_MSG = "rm.classification.unclassified";
|
||||
public static final ClassificationLevel UNCLASSIFIED = new ClassificationLevel(UNCLASSIFIED_ID, UNCLASSIFIED_MSG);
|
||||
|
||||
/** An immutable list of classification levels ordered from most to least secure. */
|
||||
private ImmutableList<ClassificationLevel> classificationLevels;
|
||||
|
||||
/**
|
||||
* Constructor that stores an immutable copy of the given levels.
|
||||
*
|
||||
* Store an immutable copy of the given classification levels.
|
||||
*
|
||||
* @param classificationLevels A list of classification levels ordered from most to least secure.
|
||||
*/
|
||||
public ClassificationLevelManager(List<ClassificationLevel> classificationLevels)
|
||||
public void setClassificationLevels(List<ClassificationLevel> classificationLevels)
|
||||
{
|
||||
List<ClassificationLevel> temp = new ArrayList<ClassificationLevel>(classificationLevels);
|
||||
temp.add(temp.size(), UNCLASSIFIED);
|
||||
List<ClassificationLevel> temp = new ArrayList<ClassificationLevel>(classificationLevels);
|
||||
temp.add(temp.size(), UNCLASSIFIED);
|
||||
this.classificationLevels = ImmutableList.copyOf(temp);
|
||||
}
|
||||
|
||||
@@ -66,7 +65,7 @@ public class ClassificationLevelManager
|
||||
|
||||
/**
|
||||
* Get a <code>ClassificationLevel</code> using its id.
|
||||
*
|
||||
*
|
||||
* @param id The id of a classification level.
|
||||
* @return The classification level.
|
||||
* @throws LevelIdNotFound If the classification level cannot be found.
|
||||
|
@@ -25,7 +25,7 @@ import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationS
|
||||
|
||||
/**
|
||||
* Container for the configured {@link ClassificationReason} objects.
|
||||
*
|
||||
*
|
||||
* @author tpage
|
||||
*/
|
||||
public class ClassificationReasonManager
|
||||
@@ -34,11 +34,11 @@ public class ClassificationReasonManager
|
||||
private ImmutableList<ClassificationReason> classificationReasons;
|
||||
|
||||
/**
|
||||
* Constructor that stores an immutable copy of the given reasons.
|
||||
*
|
||||
* Store an immutable copy of the given reasons.
|
||||
*
|
||||
* @param classificationReasons The classification reasons.
|
||||
*/
|
||||
public ClassificationReasonManager(Collection<ClassificationReason> classificationReasons)
|
||||
public void setClassificationReasons(Collection<ClassificationReason> classificationReasons)
|
||||
{
|
||||
this.classificationReasons = ImmutableList.copyOf(classificationReasons);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class ClassificationReasonManager
|
||||
|
||||
/**
|
||||
* Get a <code>ClassificationReason</code> using its id.
|
||||
*
|
||||
*
|
||||
* @param id The id of a classification reason.
|
||||
* @return The classification reason.
|
||||
* @throws ReasonIdNotFound If the classification reason cannot be found.
|
||||
|
@@ -19,13 +19,9 @@
|
||||
package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.InvalidNode;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.ReasonIdNotFound;
|
||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* The Classification Service supports the 'Classified Records' feature, whereby Alfresco
|
||||
@@ -44,14 +40,6 @@ public interface ClassificationService
|
||||
* and therefore access to the most restricted documents).
|
||||
*/
|
||||
List<ClassificationLevel> getClassificationLevels();
|
||||
|
||||
/**
|
||||
* Returns the current classification level of a given node.
|
||||
*
|
||||
* @param nodeRef node reference
|
||||
* @return {@link ClassificationLevel} classification level, unclassified if none
|
||||
*/
|
||||
ClassificationLevel getCurrentClassification(NodeRef nodeRef);
|
||||
|
||||
/**
|
||||
* Returns an immutable list of the defined classification reasons.
|
||||
@@ -59,21 +47,6 @@ public interface ClassificationService
|
||||
*/
|
||||
List<ClassificationReason> getClassificationReasons();
|
||||
|
||||
/**
|
||||
* Classify a piece of content.
|
||||
*
|
||||
* @param classificationLevelId The security clearance needed to access the content.
|
||||
* @param classificationAuthority The name of the authority responsible for the classification of this content.
|
||||
* @param classificationReasonIds A non-empty set of ids of reasons for classifying the content in this way.
|
||||
* @param content The node to classify.
|
||||
* @throws LevelIdNotFound If the supplied level id is not found.
|
||||
* @throws ReasonIdNotFound If any of the supplied reason ids are not found.
|
||||
* @throws InvalidNodeRefException If the node could not be found.
|
||||
* @throws InvalidNode If the supplied node is not a content node.
|
||||
*/
|
||||
void classifyContent(String classificationLevelId, String classificationAuthority, Set<String> classificationReasonIds, NodeRef content)
|
||||
throws LevelIdNotFound, ReasonIdNotFound, InvalidNodeRefException, InvalidNode;
|
||||
|
||||
/**
|
||||
* Gets the unclassified {@link ClassificationLevel}.
|
||||
* @return the unclassified classification level
|
||||
|
@@ -18,9 +18,20 @@
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MissingConfiguration;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
|
||||
@@ -30,25 +41,42 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
* @author Neil Mc Erlean
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ClassificationServiceBootstrap extends AbstractLifecycleBean
|
||||
public class ClassificationServiceBootstrap extends AbstractLifecycleBean implements ClassifiedContentModel
|
||||
{
|
||||
/** Logging utility for the class. */
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationServiceBootstrap.class);
|
||||
|
||||
private final AuthenticationUtil authenticationUtil;
|
||||
private final ClassificationServiceImpl classificationServiceImpl;
|
||||
private final SecurityClearanceServiceImpl securityClearanceServiceImpl;
|
||||
private final TransactionService transactionService;
|
||||
private AttributeService attributeService;
|
||||
/** The classification levels currently configured in this server. */
|
||||
private ClassificationLevelManager classificationLevelManager = new ClassificationLevelManager();
|
||||
/** The classification reasons currently configured in this server. */
|
||||
private ClassificationReasonManager classificationReasonManager = new ClassificationReasonManager();
|
||||
/** The clearance levels currently configured in this server. */
|
||||
private ClearanceLevelManager clearanceLevelManager = new ClearanceLevelManager();
|
||||
private ClassificationServiceDAO classificationServiceDAO;
|
||||
|
||||
public ClassificationServiceBootstrap(AuthenticationUtil authUtil,
|
||||
ClassificationServiceImpl cService,
|
||||
SecurityClearanceServiceImpl securityClearanceServiceImpl,
|
||||
TransactionService txService)
|
||||
TransactionService txService,
|
||||
AttributeService attributeService,
|
||||
ClassificationServiceDAO classificationServiceDAO)
|
||||
{
|
||||
this.authenticationUtil = authUtil;
|
||||
this.classificationServiceImpl = cService;
|
||||
this.securityClearanceServiceImpl = securityClearanceServiceImpl;
|
||||
this.transactionService = txService;
|
||||
this.authenticationUtil = authUtil;
|
||||
this.transactionService = txService;
|
||||
this.attributeService = attributeService;
|
||||
this.classificationServiceDAO = classificationServiceDAO;
|
||||
}
|
||||
|
||||
@Override protected void onBootstrap(ApplicationEvent event)
|
||||
/** Set the object from which configuration options will be read. */
|
||||
public void setClassificationServiceDAO(ClassificationServiceDAO classificationServiceDAO) { this.classificationServiceDAO = classificationServiceDAO; }
|
||||
public void setAttributeService(AttributeService attributeService) { this.attributeService = attributeService; }
|
||||
|
||||
public ClassificationLevelManager getClassificationLevelManager() { return classificationLevelManager; }
|
||||
public ClassificationReasonManager getClassificationReasonManager() { return classificationReasonManager; }
|
||||
public ClearanceLevelManager getClearanceLevelManager() { return clearanceLevelManager; }
|
||||
|
||||
@Override public void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
authenticationUtil.runAsSystem(new org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork<Void>()
|
||||
{
|
||||
@@ -58,8 +86,9 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean
|
||||
{
|
||||
public Void execute()
|
||||
{
|
||||
classificationServiceImpl.initialise();
|
||||
securityClearanceServiceImpl.initialise();
|
||||
initConfiguredClassificationLevels();
|
||||
initConfiguredClassificationReasons();
|
||||
initConfiguredClearanceLevels(classificationLevelManager.getClassificationLevels());
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -69,6 +98,144 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the system's classification levels by loading the values from a configuration file and storing them
|
||||
* in the attribute service and the {@link ClassificationLevelManager}.
|
||||
*/
|
||||
protected void initConfiguredClassificationLevels()
|
||||
{
|
||||
final List<ClassificationLevel> allPersistedLevels = getPersistedLevels();
|
||||
final List<ClassificationLevel> configurationLevels = getConfigurationLevels();
|
||||
|
||||
// Note! We cannot log the level names or even the size of these lists for security reasons.
|
||||
LOGGER.debug("Persisted classification levels: {}", loggableStatusOf(allPersistedLevels));
|
||||
LOGGER.debug("Classpath classification levels: {}", loggableStatusOf(configurationLevels));
|
||||
|
||||
if (configurationLevels == null || configurationLevels.isEmpty())
|
||||
{
|
||||
throw new MissingConfiguration("Classification level configuration is missing.");
|
||||
}
|
||||
else if (!configurationLevels.equals(allPersistedLevels))
|
||||
{
|
||||
attributeService.setAttribute((Serializable) configurationLevels, LEVELS_KEY);
|
||||
this.classificationLevelManager.setClassificationLevels(configurationLevels);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.classificationLevelManager.setClassificationLevels(allPersistedLevels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list (in descending order) of classification levels - as persisted in the system.
|
||||
* @return the list of classification levels if they have been persisted, else {@code null}.
|
||||
*/
|
||||
private List<ClassificationLevel> getPersistedLevels()
|
||||
{
|
||||
return authenticationUtil.runAsSystem(new RunAsWork<List<ClassificationLevel>>()
|
||||
{
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ClassificationLevel> doWork() throws Exception
|
||||
{
|
||||
return (List<ClassificationLevel>) attributeService.getAttribute(LEVELS_KEY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Gets the list (in descending order) of classification levels - as defined in the system configuration. */
|
||||
private List<ClassificationLevel> getConfigurationLevels()
|
||||
{
|
||||
return classificationServiceDAO.getConfiguredLevels();
|
||||
}
|
||||
|
||||
private static boolean isEmpty(List<?> l) { return l == null || l.isEmpty(); }
|
||||
|
||||
/** Helper method for debug-logging of sensitive lists. */
|
||||
private String loggableStatusOf(List<?> l)
|
||||
{
|
||||
if (l == null) { return "null"; }
|
||||
else if (l.isEmpty()) { return "empty"; }
|
||||
else { return "non-empty"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the system's classification reasons by loading the values from a configuration file and storing them
|
||||
* in the attribute service and the {@link ClassificationReasonManager}.
|
||||
*/
|
||||
protected void initConfiguredClassificationReasons()
|
||||
{
|
||||
final List<ClassificationReason> persistedReasons = getPersistedReasons();
|
||||
final List<ClassificationReason> classpathReasons = getConfigurationReasons();
|
||||
|
||||
// Note! We cannot log the reasons or even the size of these lists for security reasons.
|
||||
LOGGER.debug("Persisted classification reasons: {}", loggableStatusOf(persistedReasons));
|
||||
LOGGER.debug("Classpath classification reasons: {}", loggableStatusOf(classpathReasons));
|
||||
|
||||
if (isEmpty(persistedReasons))
|
||||
{
|
||||
if (isEmpty(classpathReasons))
|
||||
{
|
||||
throw new MissingConfiguration("Classification reason configuration is missing.");
|
||||
}
|
||||
attributeService.setAttribute((Serializable) classpathReasons, REASONS_KEY);
|
||||
this.classificationReasonManager.setClassificationReasons(classpathReasons);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isEmpty(classpathReasons) || !classpathReasons.equals(persistedReasons))
|
||||
{
|
||||
LOGGER.warn("Classification reasons configured in classpath do not match those stored in Alfresco. "
|
||||
+ "Alfresco will use the unchanged values stored in the database.");
|
||||
// RM-2073 says that we should log a warning and proceed normally.
|
||||
}
|
||||
this.classificationReasonManager.setClassificationReasons(persistedReasons);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of classification reasons as persisted in the system.
|
||||
* @return the list of classification reasons if they have been persisted, else {@code null}.
|
||||
*/
|
||||
private List<ClassificationReason> getPersistedReasons()
|
||||
{
|
||||
return authenticationUtil.runAsSystem(new RunAsWork<List<ClassificationReason>>()
|
||||
{
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ClassificationReason> doWork() throws Exception
|
||||
{
|
||||
return (List<ClassificationReason>) attributeService.getAttribute(REASONS_KEY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Gets the list of classification reasons - as defined and ordered in the system configuration. */
|
||||
private List<ClassificationReason> getConfigurationReasons()
|
||||
{
|
||||
return classificationServiceDAO.getConfiguredReasons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise and create a {@link ClearanceLevelManager}.
|
||||
*
|
||||
* @param classificationLevels The list of classification levels to use to create clearance levels from.
|
||||
*/
|
||||
protected void initConfiguredClearanceLevels(ImmutableList<ClassificationLevel> classificationLevels)
|
||||
{
|
||||
List<ClearanceLevel> clearanceLevels = new ArrayList<>();
|
||||
for (ClassificationLevel classificationLevel : classificationLevels)
|
||||
{
|
||||
String displayLabelKey = classificationLevel.getDisplayLabelKey();
|
||||
if (classificationLevel.equals(ClassificationLevelManager.UNCLASSIFIED))
|
||||
{
|
||||
displayLabelKey = "rm.classification.noClearance";
|
||||
}
|
||||
clearanceLevels.add(new ClearanceLevel(classificationLevel, displayLabelKey));
|
||||
}
|
||||
this.clearanceLevelManager.setClearanceLevels(clearanceLevels);
|
||||
}
|
||||
|
||||
@Override protected void onShutdown(ApplicationEvent event)
|
||||
{
|
||||
// Intentionally empty.
|
||||
|
@@ -19,30 +19,15 @@
|
||||
package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
|
||||
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
|
||||
import static org.alfresco.util.ParameterCheck.mandatory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.InvalidNode;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.MissingConfiguration;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.ReasonIdNotFound;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Neil Mc Erlean
|
||||
@@ -51,160 +36,23 @@ import org.slf4j.LoggerFactory;
|
||||
public class ClassificationServiceImpl extends ServiceBaseImpl
|
||||
implements ClassificationService, ClassifiedContentModel
|
||||
{
|
||||
private static final Serializable[] LEVELS_KEY = new String[] { "org.alfresco",
|
||||
"module.org_alfresco_module_rm",
|
||||
"classification.levels" };
|
||||
private static final Serializable[] REASONS_KEY = new String[] { "org.alfresco",
|
||||
"module.org_alfresco_module_rm",
|
||||
"classification.reasons" };
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationServiceImpl.class);
|
||||
|
||||
private AttributeService attributeService; // TODO What about other code (e.g. REST API) accessing the AttrService?
|
||||
private NodeService nodeService;
|
||||
private ClassificationServiceDAO classificationServiceDao;
|
||||
|
||||
/** The classification levels currently configured in this server. */
|
||||
private ClassificationLevelManager levelManager;
|
||||
/** The classification reasons currently configured in this server. */
|
||||
private ClassificationReasonManager reasonManager;
|
||||
private ClassificationServiceBootstrap classificationServiceBootstrap;
|
||||
|
||||
public void setAttributeService(AttributeService service) { this.attributeService = service; }
|
||||
public void setNodeService(NodeService service) { this.nodeService = service; }
|
||||
|
||||
/** Set the object from which configuration options will be read. */
|
||||
public void setClassificationServiceDAO(ClassificationServiceDAO classificationServiceDao) { this.classificationServiceDao = classificationServiceDao; }
|
||||
|
||||
void initialise()
|
||||
public void setClassificationServiceBootstrap(ClassificationServiceBootstrap classificationServiceBootstrap)
|
||||
{
|
||||
initConfiguredClassificationLevels();
|
||||
initConfiguredClassificationReasons();
|
||||
this.classificationServiceBootstrap = classificationServiceBootstrap;
|
||||
}
|
||||
|
||||
protected void initConfiguredClassificationLevels()
|
||||
/** Store the references to the classification level and reason managers in this class. */
|
||||
public void init()
|
||||
{
|
||||
final List<ClassificationLevel> allPersistedLevels = getPersistedLevels();
|
||||
final List<ClassificationLevel> configurationLevels = getConfigurationLevels();
|
||||
|
||||
// Note! We cannot log the level names or even the size of these lists for security reasons.
|
||||
LOGGER.debug("Persisted classification levels: {}", loggableStatusOf(allPersistedLevels));
|
||||
LOGGER.debug("Classpath classification levels: {}", loggableStatusOf(configurationLevels));
|
||||
|
||||
if (configurationLevels == null || configurationLevels.isEmpty())
|
||||
{
|
||||
throw new MissingConfiguration("Classification level configuration is missing.");
|
||||
}
|
||||
else if (!configurationLevels.equals(allPersistedLevels))
|
||||
{
|
||||
attributeService.setAttribute((Serializable) configurationLevels, LEVELS_KEY);
|
||||
this.levelManager = new ClassificationLevelManager(configurationLevels);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.levelManager = new ClassificationLevelManager(allPersistedLevels);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initConfiguredClassificationReasons()
|
||||
{
|
||||
final List<ClassificationReason> persistedReasons = getPersistedReasons();
|
||||
final List<ClassificationReason> classpathReasons = getConfigurationReasons();
|
||||
|
||||
// Note! We cannot log the reasons or even the size of these lists for security reasons.
|
||||
LOGGER.debug("Persisted classification reasons: {}", loggableStatusOf(persistedReasons));
|
||||
LOGGER.debug("Classpath classification reasons: {}", loggableStatusOf(classpathReasons));
|
||||
|
||||
if (isEmpty(persistedReasons))
|
||||
{
|
||||
if (isEmpty(classpathReasons))
|
||||
{
|
||||
throw new MissingConfiguration("Classification reason configuration is missing.");
|
||||
}
|
||||
attributeService.setAttribute((Serializable) classpathReasons, REASONS_KEY);
|
||||
this.reasonManager = new ClassificationReasonManager(classpathReasons);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isEmpty(classpathReasons) || !classpathReasons.equals(persistedReasons))
|
||||
{
|
||||
LOGGER.warn("Classification reasons configured in classpath do not match those stored in Alfresco. "
|
||||
+ "Alfresco will use the unchanged values stored in the database.");
|
||||
// RM-2073 says that we should log a warning and proceed normally.
|
||||
}
|
||||
this.reasonManager = new ClassificationReasonManager(persistedReasons);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isEmpty(List<?> l) { return l == null || l.isEmpty(); }
|
||||
|
||||
/** Helper method for debug-logging of sensitive lists. */
|
||||
private String loggableStatusOf(List<?> l)
|
||||
{
|
||||
if (l == null) { return "null"; }
|
||||
else if (l.isEmpty()) { return "empty"; }
|
||||
else { return "non-empty"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list (in descending order) of classification levels - as persisted in the system.
|
||||
* @return the list of classification levels if they have been persisted, else {@code null}.
|
||||
*/
|
||||
List<ClassificationLevel> getPersistedLevels()
|
||||
{
|
||||
return authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<List<ClassificationLevel>>()
|
||||
{
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ClassificationLevel> doWork() throws Exception
|
||||
{
|
||||
return (List<ClassificationLevel>) attributeService.getAttribute(LEVELS_KEY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Gets the list (in descending order) of classification levels - as defined in the system configuration. */
|
||||
List<ClassificationLevel> getConfigurationLevels()
|
||||
{
|
||||
return classificationServiceDao.getConfiguredLevels();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService#getCurrentClassification(org.alfresco.service.cmr.repository.NodeRef)
|
||||
*/
|
||||
public ClassificationLevel getCurrentClassification(NodeRef nodeRef)
|
||||
{
|
||||
// by default everything is unclassified
|
||||
ClassificationLevel result = ClassificationLevelManager.UNCLASSIFIED;
|
||||
|
||||
if (nodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED))
|
||||
{
|
||||
String classificationId = (String)nodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION);
|
||||
result = levelManager.findLevelById(classificationId);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the list of classification reasons as persisted in the system.
|
||||
* @return the list of classification reasons if they have been persisted, else {@code null}.
|
||||
*/
|
||||
List<ClassificationReason> getPersistedReasons()
|
||||
{
|
||||
return authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<List<ClassificationReason>>()
|
||||
{
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<ClassificationReason> doWork() throws Exception
|
||||
{
|
||||
return (List<ClassificationReason>) attributeService.getAttribute(REASONS_KEY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Gets the list of classification reasons - as defined and ordered in the system configuration. */
|
||||
List<ClassificationReason> getConfigurationReasons()
|
||||
{
|
||||
return classificationServiceDao.getConfiguredReasons();
|
||||
levelManager = classificationServiceBootstrap.getClassificationLevelManager();
|
||||
reasonManager = classificationServiceBootstrap.getClassificationReasonManager();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,58 +89,9 @@ public class ClassificationServiceImpl extends ServiceBaseImpl
|
||||
Collections.unmodifiableList(reasonManager.getClassificationReasons());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classifyContent(String classificationLevelId, String classificationAuthority,
|
||||
Set<String> classificationReasonIds, NodeRef content)
|
||||
{
|
||||
checkNotBlank("classificationLevelId", classificationLevelId);
|
||||
checkNotBlank("classificationAuthority", classificationAuthority);
|
||||
mandatory("classificationReasonIds", classificationReasonIds);
|
||||
mandatory("content", content);
|
||||
|
||||
if (!dictionaryService.isSubClass(nodeService.getType(content), ContentModel.TYPE_CONTENT))
|
||||
{
|
||||
throw new InvalidNode(content, "The supplied node is not a content node.");
|
||||
}
|
||||
if (nodeService.hasAspect(content, ASPECT_CLASSIFIED))
|
||||
{
|
||||
throw new UnsupportedOperationException(
|
||||
"The content has already been classified. Reclassification is currently not supported.");
|
||||
}
|
||||
|
||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
||||
// Check the classification level id - an exception will be thrown if the id cannot be found
|
||||
getClassificationLevelById(classificationLevelId);
|
||||
|
||||
// Initial classification id
|
||||
if (nodeService.getProperty(content, PROP_INITIAL_CLASSIFICATION) == null)
|
||||
{
|
||||
properties.put(PROP_INITIAL_CLASSIFICATION, classificationLevelId);
|
||||
}
|
||||
|
||||
// Current classification id
|
||||
properties.put(PROP_CURRENT_CLASSIFICATION, classificationLevelId);
|
||||
|
||||
// Classification authority
|
||||
properties.put(PROP_CLASSIFICATION_AUTHORITY, classificationAuthority);
|
||||
|
||||
// Classification reason ids
|
||||
HashSet<String> classificationReasons = new HashSet<>();
|
||||
for (String classificationReasonId : classificationReasonIds)
|
||||
{
|
||||
// Check the classification reason id - an exception will be thrown if the id cannot be found
|
||||
getClassificationReasonById(classificationReasonId);
|
||||
classificationReasons.add(classificationReasonId);
|
||||
}
|
||||
properties.put(PROP_CLASSIFICATION_REASONS, classificationReasons);
|
||||
|
||||
// Add aspect
|
||||
nodeService.addAspect(content, ASPECT_CLASSIFIED, properties);
|
||||
}
|
||||
|
||||
@Override public ClassificationLevel getUnclassifiedClassificationLevel()
|
||||
{
|
||||
return ClassificationLevelManager.UNCLASSIFIED;
|
||||
return ClassificationLevelManager.UNCLASSIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -18,12 +18,10 @@
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
|
||||
/**
|
||||
* Container for the configured {@link ClearanceLevel} objects.
|
||||
@@ -32,22 +30,20 @@ import com.google.common.collect.ImmutableList;
|
||||
*/
|
||||
public class ClearanceLevelManager
|
||||
{
|
||||
private static String NO_CLEARANCE_MSG = "rm.classification.noClearance";
|
||||
public static final ClearanceLevel NO_CLEARANCE = new ClearanceLevel(ClassificationLevelManager.UNCLASSIFIED, NO_CLEARANCE_MSG);
|
||||
|
||||
private static String NO_CLEARANCE_MSG = "rm.classification.noClearance";
|
||||
public static final ClearanceLevel NO_CLEARANCE = new ClearanceLevel(ClassificationLevelManager.UNCLASSIFIED, NO_CLEARANCE_MSG);
|
||||
|
||||
/** An immutable list of clearance levels ordered from most to least secure. */
|
||||
private ImmutableList<ClearanceLevel> clearanceLevels;
|
||||
|
||||
/**
|
||||
* Constructor that stores an immutable copy of the given levels.
|
||||
* Store an immutable copy of the given levels.
|
||||
*
|
||||
* @param clearanceLevels A list of clearance levels ordered from most to least secure.
|
||||
*/
|
||||
public ClearanceLevelManager(List<ClearanceLevel> clearanceLevels)
|
||||
public void setClearanceLevels(List<ClearanceLevel> clearanceLevels)
|
||||
{
|
||||
List<ClearanceLevel> temp = new ArrayList<ClearanceLevel>(clearanceLevels);
|
||||
temp.add(temp.size(), NO_CLEARANCE);
|
||||
this.clearanceLevels = ImmutableList.copyOf(temp);
|
||||
this.clearanceLevels = ImmutableList.copyOf(clearanceLevels);
|
||||
}
|
||||
|
||||
/** @return An immutable list of clearance levels ordered from most to least secure. */
|
||||
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.classification;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.InvalidNode;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.ReasonIdNotFound;
|
||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* A service to handle the classification of content.
|
||||
*
|
||||
* @author tpage
|
||||
*/
|
||||
public interface ContentClassificationService
|
||||
{
|
||||
/**
|
||||
* Returns the current classification level of a given node.
|
||||
*
|
||||
* @param nodeRef node reference
|
||||
* @return {@link ClassificationLevel} classification level, unclassified if none
|
||||
*/
|
||||
ClassificationLevel getCurrentClassification(NodeRef nodeRef);
|
||||
|
||||
/**
|
||||
* Classify a piece of content.
|
||||
*
|
||||
* @param classificationLevelId The security clearance needed to access the content.
|
||||
* @param classificationAuthority The name of the authority responsible for the classification of this content.
|
||||
* @param classificationReasonIds A non-empty set of ids of reasons for classifying the content in this way.
|
||||
* @param content The node to classify.
|
||||
* @throws LevelIdNotFound If the supplied level id is not found.
|
||||
* @throws ReasonIdNotFound If any of the supplied reason ids are not found.
|
||||
* @throws InvalidNodeRefException If the node could not be found.
|
||||
* @throws InvalidNode If the supplied node is not a content node.
|
||||
*/
|
||||
void classifyContent(String classificationLevelId, String classificationAuthority, Set<String> classificationReasonIds, NodeRef content)
|
||||
throws LevelIdNotFound, ReasonIdNotFound, InvalidNodeRefException, InvalidNode;
|
||||
|
||||
/**
|
||||
* Indicates whether the currently authenticated user has clearance to see the
|
||||
* provided node.
|
||||
* <p>
|
||||
* Note that users, regardless of their clearance level, are always cleared to see a node that has no classification
|
||||
* applied.
|
||||
*
|
||||
* @param nodeRef node reference
|
||||
* @return boolean true if cleared to see node, false otherwise
|
||||
*/
|
||||
boolean hasClearance(NodeRef nodeRef);
|
||||
}
|
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.classification;
|
||||
|
||||
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
|
||||
import static org.alfresco.util.ParameterCheck.mandatory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.InvalidNode;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* A service to handle the classification of content.
|
||||
*
|
||||
* @author tpage
|
||||
*/
|
||||
public class ContentClassificationServiceImpl extends ServiceBaseImpl implements ContentClassificationService,
|
||||
ClassifiedContentModel
|
||||
{
|
||||
private ClassificationLevelManager levelManager;
|
||||
private ClassificationReasonManager reasonManager;
|
||||
private NodeService nodeService;
|
||||
private DictionaryService dictionaryService;
|
||||
private SecurityClearanceService securityClearanceService;
|
||||
private ClassificationServiceBootstrap classificationServiceBootstrap;
|
||||
|
||||
public void setLevelManager(ClassificationLevelManager levelManager) { this.levelManager = levelManager; }
|
||||
public void setReasonManager(ClassificationReasonManager reasonManager) { this.reasonManager = reasonManager; }
|
||||
public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; }
|
||||
public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; }
|
||||
public void setSecurityClearanceService(SecurityClearanceService securityClearanceService) { this.securityClearanceService = securityClearanceService; }
|
||||
public void setClassificationServiceBootstrap(ClassificationServiceBootstrap classificationServiceBootstrap) { this.classificationServiceBootstrap = classificationServiceBootstrap; }
|
||||
|
||||
public void init()
|
||||
{
|
||||
this.levelManager = classificationServiceBootstrap.getClassificationLevelManager();
|
||||
this.reasonManager = classificationServiceBootstrap.getClassificationReasonManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassificationLevel getCurrentClassification(NodeRef nodeRef)
|
||||
{
|
||||
// by default everything is unclassified
|
||||
ClassificationLevel result = ClassificationLevelManager.UNCLASSIFIED;
|
||||
|
||||
if (nodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED))
|
||||
{
|
||||
String classificationId = (String)nodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION);
|
||||
result = levelManager.findLevelById(classificationId);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@Override
|
||||
public void classifyContent(String classificationLevelId, String classificationAuthority,
|
||||
Set<String> classificationReasonIds, NodeRef content)
|
||||
{
|
||||
checkNotBlank("classificationLevelId", classificationLevelId);
|
||||
checkNotBlank("classificationAuthority", classificationAuthority);
|
||||
mandatory("classificationReasonIds", classificationReasonIds);
|
||||
mandatory("content", content);
|
||||
|
||||
if (!dictionaryService.isSubClass(nodeService.getType(content), ContentModel.TYPE_CONTENT))
|
||||
{
|
||||
throw new InvalidNode(content, "The supplied node is not a content node.");
|
||||
}
|
||||
if (nodeService.hasAspect(content, ASPECT_CLASSIFIED))
|
||||
{
|
||||
throw new UnsupportedOperationException(
|
||||
"The content has already been classified. Reclassification is currently not supported.");
|
||||
}
|
||||
|
||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
||||
// Check the classification level id - an exception will be thrown if the id cannot be found
|
||||
levelManager.findLevelById(classificationLevelId);
|
||||
|
||||
// Initial classification id
|
||||
if (nodeService.getProperty(content, PROP_INITIAL_CLASSIFICATION) == null)
|
||||
{
|
||||
properties.put(PROP_INITIAL_CLASSIFICATION, classificationLevelId);
|
||||
}
|
||||
|
||||
// Current classification id
|
||||
properties.put(PROP_CURRENT_CLASSIFICATION, classificationLevelId);
|
||||
|
||||
// Classification authority
|
||||
properties.put(PROP_CLASSIFICATION_AUTHORITY, classificationAuthority);
|
||||
|
||||
// Classification reason ids
|
||||
HashSet<String> classificationReasons = new HashSet<>();
|
||||
for (String classificationReasonId : classificationReasonIds)
|
||||
{
|
||||
// Check the classification reason id - an exception will be thrown if the id cannot be found
|
||||
reasonManager.findReasonById(classificationReasonId);
|
||||
classificationReasons.add(classificationReasonId);
|
||||
}
|
||||
properties.put(PROP_CLASSIFICATION_REASONS, classificationReasons);
|
||||
|
||||
// Add aspect
|
||||
nodeService.addAspect(content, ASPECT_CLASSIFIED, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasClearance(NodeRef nodeRef)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
// Get the node's current classification
|
||||
ClassificationLevel currentClassification = getCurrentClassification(nodeRef);
|
||||
if (ClassificationLevelManager.UNCLASSIFIED.equals(currentClassification))
|
||||
{
|
||||
// since the node is not classified user has clearance
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the user's security clearance
|
||||
SecurityClearance securityClearance = securityClearanceService.getUserSecurityClearance();
|
||||
if (!ClearanceLevelManager.NO_CLEARANCE.equals(securityClearance.getClearanceLevel()))
|
||||
{
|
||||
// get the users highest classification clearance
|
||||
ClassificationLevel highestClassification = securityClearance.getClearanceLevel().getHighestClassificationLevel();
|
||||
|
||||
// if classification is less than or equal to highest classification then user has clearance
|
||||
List<ClassificationLevel> allClassificationLevels = levelManager.getClassificationLevels();
|
||||
int highestIndex = allClassificationLevels.indexOf(highestClassification);
|
||||
int currentIndex = allClassificationLevels.indexOf(currentClassification);
|
||||
|
||||
if (highestIndex <= currentIndex)
|
||||
{
|
||||
// user has clearance
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -19,7 +19,6 @@
|
||||
package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
|
||||
import org.alfresco.query.PagingResults;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.security.NoSuchPersonException;
|
||||
|
||||
import java.util.List;
|
||||
@@ -33,18 +32,6 @@ import java.util.List;
|
||||
*/
|
||||
public interface SecurityClearanceService
|
||||
{
|
||||
/**
|
||||
* Indicates whether the currently authenticated user has clearance to see the
|
||||
* provided node.
|
||||
* <p>
|
||||
* Note that users, regardless of their clearance level, are always cleared to see a node that has no classification
|
||||
* applied.
|
||||
*
|
||||
* @param nodeRef node reference
|
||||
* @return boolean true if cleared to see node, false otherwise
|
||||
*/
|
||||
boolean hasClearance(NodeRef nodeRef);
|
||||
|
||||
/**
|
||||
* Get the currently authenticated user's security clearance.
|
||||
*
|
||||
|
@@ -25,6 +25,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceException.LevelIdNotFound;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
|
||||
import org.alfresco.query.PagingRequest;
|
||||
import org.alfresco.query.PagingResults;
|
||||
@@ -43,75 +45,23 @@ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements Sec
|
||||
{
|
||||
/** The clearance levels currently configured in this server. */
|
||||
private ClearanceLevelManager clearanceManager;
|
||||
|
||||
private ClassificationService classificationService;
|
||||
private PersonService personService;
|
||||
/** The object containing the {@link ClassificationLevel}s in the system. */
|
||||
private ClassificationLevelManager classificationLevelManager;
|
||||
private PersonService personService;
|
||||
private ClassificationServiceBootstrap classificationServiceBootstrap;
|
||||
|
||||
public void setClearanceManager(ClearanceLevelManager clearanceManager) { this.clearanceManager = clearanceManager; }
|
||||
public void setClassificationService(ClassificationService service) { this.classificationService = service; }
|
||||
public void setClassificationLevelManager(ClassificationLevelManager classificationLevelManager) { this.classificationLevelManager = classificationLevelManager; }
|
||||
public void setPersonService(PersonService service) { this.personService = service; }
|
||||
public void setClassificationServiceBootstrap(ClassificationServiceBootstrap classificationServiceBootstrap) { this.classificationServiceBootstrap = classificationServiceBootstrap; }
|
||||
|
||||
/**
|
||||
* Initialise and create a {@link ClearanceLevelManager}. This assumes that the {@link ClassificationService} has
|
||||
* already been initialised.
|
||||
*/
|
||||
void initialise()
|
||||
/** Store the references to the classification and clearance level managers in this class. */
|
||||
public void init()
|
||||
{
|
||||
ArrayList<ClearanceLevel> clearanceLevels = new ArrayList<ClearanceLevel>();
|
||||
List<ClassificationLevel> classificationLevels = classificationService.getClassificationLevels();
|
||||
for (ClassificationLevel classificationLevel : classificationLevels)
|
||||
{
|
||||
if (!ClassificationLevelManager.UNCLASSIFIED.equals(classificationLevel))
|
||||
{
|
||||
clearanceLevels.add(new ClearanceLevel(classificationLevel, classificationLevel.getDisplayLabelKey()));
|
||||
}
|
||||
}
|
||||
this.clearanceManager = new ClearanceLevelManager(clearanceLevels);
|
||||
this.classificationLevelManager = classificationServiceBootstrap.getClassificationLevelManager();
|
||||
this.clearanceManager = classificationServiceBootstrap.getClearanceLevelManager();
|
||||
}
|
||||
|
||||
/** Get the clearance manager (for use in unit testing). */
|
||||
protected ClearanceLevelManager getClearanceManager() { return clearanceManager; }
|
||||
|
||||
/**
|
||||
* @see org.alfresco.module.org_alfresco_module_rm.classification.SecurityClearanceService#hasClearance(org.alfresco.service.cmr.repository.NodeRef)
|
||||
*/
|
||||
@Override
|
||||
public boolean hasClearance(NodeRef nodeRef)
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
// get the nodes current classification
|
||||
ClassificationLevel currentClassification = classificationService.getCurrentClassification(nodeRef);
|
||||
if (ClassificationLevelManager.UNCLASSIFIED.equals(currentClassification))
|
||||
{
|
||||
// since the node is not classified user has clearance
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get the users security clearance
|
||||
SecurityClearance securityClearance = getUserSecurityClearance();
|
||||
if (!ClearanceLevelManager.NO_CLEARANCE.equals(securityClearance.getClearanceLevel()))
|
||||
{
|
||||
// get the users highest classification clearance
|
||||
ClassificationLevel highestClassification = securityClearance.getClearanceLevel().getHighestClassificationLevel();
|
||||
|
||||
// if classification is less than or equal to highest classification then user has clearance
|
||||
List<ClassificationLevel> allClassificationLevels = classificationService.getClassificationLevels();
|
||||
int highestIndex = allClassificationLevels.indexOf(highestClassification);
|
||||
int currentIndex = allClassificationLevels.indexOf(currentClassification);
|
||||
|
||||
if (highestIndex <= currentIndex)
|
||||
{
|
||||
// user has clearance
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityClearance getUserSecurityClearance()
|
||||
{
|
||||
@@ -123,7 +73,7 @@ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements Sec
|
||||
|
||||
/**
|
||||
* Gets the users security clearnace.
|
||||
*
|
||||
*
|
||||
* @param userName user name
|
||||
* @return {@link SecurityClearance} provides information about the user and their clearance level
|
||||
*/
|
||||
@@ -132,21 +82,14 @@ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements Sec
|
||||
final NodeRef personNode = personService.getPerson(userName, false);
|
||||
final PersonInfo personInfo = personService.getPerson(personNode);
|
||||
|
||||
final ClassificationLevel classificationLevel;
|
||||
|
||||
ClearanceLevel clearanceLevel = ClearanceLevelManager.NO_CLEARANCE;
|
||||
if (nodeService.hasAspect(personNode, ASPECT_SECURITY_CLEARANCE))
|
||||
{
|
||||
final String clearanceLevelValue = (String)nodeService.getProperty(personNode, PROP_CLEARANCE_LEVEL);
|
||||
|
||||
classificationLevel = clearanceLevelValue == null ? classificationService.getUnclassifiedClassificationLevel() :
|
||||
classificationService.getClassificationLevelById(clearanceLevelValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
classificationLevel = classificationService.getUnclassifiedClassificationLevel();
|
||||
final String clearanceLevelId = (String)nodeService.getProperty(personNode, PROP_CLEARANCE_LEVEL);
|
||||
clearanceLevel = (clearanceLevelId == null ? ClearanceLevelManager.NO_CLEARANCE
|
||||
: clearanceManager.findLevelByClassificationLevelId(clearanceLevelId));
|
||||
}
|
||||
|
||||
ClearanceLevel clearanceLevel = clearanceManager.findLevelByClassificationLevelId(classificationLevel.getId());
|
||||
return new SecurityClearance(personInfo, clearanceLevel);
|
||||
}
|
||||
|
||||
@@ -182,6 +125,33 @@ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements Sec
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a classification can be accessed by a user with a given clearance.
|
||||
*
|
||||
* @param clearance The clearance of the user.
|
||||
* @param classificationId The classification level to look for.
|
||||
* @return {@code true} if the user can access the classification level.
|
||||
*/
|
||||
protected boolean isClearedForClassification(SecurityClearance clearance, String classificationId)
|
||||
{
|
||||
ImmutableList<ClassificationLevel> classificationLevels = classificationLevelManager.getClassificationLevels();
|
||||
|
||||
String clearanceId = clearance.getClearanceLevel().getHighestClassificationLevel().getId();
|
||||
for (ClassificationLevel classificationLevel : classificationLevels)
|
||||
{
|
||||
if (classificationLevel.getId().equals(clearanceId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (classificationLevel.getId().equals(classificationId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Neither the clearance id nor the classification id were found - something's gone wrong.
|
||||
throw new LevelIdNotFound(classificationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityClearance setUserSecurityClearance(String userName, String clearanceId)
|
||||
{
|
||||
@@ -189,9 +159,13 @@ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements Sec
|
||||
ParameterCheck.mandatoryString("clearanceId", clearanceId);
|
||||
|
||||
final NodeRef personNode = personService.getPerson(userName, false);
|
||||
// This is just used to check the current user has clearance to see the specified level; it will throw a
|
||||
// LevelIdNotFound exception if not.
|
||||
classificationService.getClassificationLevelById(clearanceId);
|
||||
|
||||
// Check the current user has clearance to see the specified level.
|
||||
SecurityClearance userSecurityClearance = getUserSecurityClearance();
|
||||
if (!isClearedForClassification(userSecurityClearance, clearanceId))
|
||||
{
|
||||
throw new LevelIdNotFound(clearanceId);
|
||||
}
|
||||
|
||||
nodeService.setProperty(personNode, PROP_CLEARANCE_LEVEL, clearanceId);
|
||||
|
||||
|
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.classification.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
@@ -34,6 +36,9 @@ public interface ClassifiedContentModel
|
||||
String CLF_URI = "http://www.alfresco.org/model/classifiedcontent/1.0";
|
||||
String CLF_PREFIX = "clf";
|
||||
|
||||
Serializable[] LEVELS_KEY = new String[] { "org.alfresco", "module.org_alfresco_module_rm", "classification.levels" };
|
||||
Serializable[] REASONS_KEY = new String[] { "org.alfresco", "module.org_alfresco_module_rm", "classification.reasons" };
|
||||
|
||||
/** Classified aspect */
|
||||
QName ASPECT_CLASSIFIED = QName.createQName(CLF_URI, "classified");
|
||||
QName PROP_INITIAL_CLASSIFICATION = QName.createQName(CLF_URI, "initialClassification");
|
||||
|
@@ -1,19 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2014 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
|
||||
* Copyright (C) 2005-2014 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.script.classification;
|
||||
@@ -28,7 +28,7 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.script.AbstractRmWebScript;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.json.JSONArray;
|
||||
@@ -50,23 +50,17 @@ public class ClassifyContentPost extends AbstractRmWebScript
|
||||
public static final String CLASSIFICATION_AUTHORITY = "classificationAuthority";
|
||||
public static final String CLASSIFICATION_REASONS = "classificationReasons";
|
||||
|
||||
/** Classification service */
|
||||
private ClassificationService classificationService;
|
||||
/** The service responsible for classifying content. */
|
||||
private ContentClassificationService contentClassificationService;
|
||||
|
||||
/**
|
||||
* @return the classificationService
|
||||
* Set the service responsible for classifying content.
|
||||
*
|
||||
* @param contentClassificationService The service responsible for classifying content.
|
||||
*/
|
||||
protected ClassificationService getClassificationService()
|
||||
public void setContentClassificationService(ContentClassificationService contentClassificationService)
|
||||
{
|
||||
return this.classificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classificationService the classificationService to set
|
||||
*/
|
||||
public void setClassificationService(ClassificationService classificationService)
|
||||
{
|
||||
this.classificationService = classificationService;
|
||||
this.contentClassificationService = contentClassificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +77,7 @@ public class ClassifyContentPost extends AbstractRmWebScript
|
||||
Set<String> classificationReasonIds = getClassificationReasonIds(jsonObject);
|
||||
NodeRef document = parseRequestForNodeRef(req);
|
||||
|
||||
getClassificationService().classifyContent(classificationLevelId, classificationAuthority, classificationReasonIds, document);
|
||||
contentClassificationService.classifyContent(classificationLevelId, classificationAuthority, classificationReasonIds, document);
|
||||
|
||||
Map<String, Object> model = new HashMap<String, Object>(1);
|
||||
model.put(SUCCESS, true);
|
||||
|
Reference in New Issue
Block a user