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:
@@ -39,9 +39,8 @@
|
||||
|
||||
<bean id="classificationService"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceImpl"
|
||||
parent="baseService">
|
||||
<property name="attributeService" ref="AttributeService"/>
|
||||
<property name="classificationServiceDAO" ref="classificationServiceDAO"/>
|
||||
parent="baseService" init-method="init">
|
||||
<property name="classificationServiceBootstrap" ref="classificationServiceBootstrap"/>
|
||||
</bean>
|
||||
|
||||
<bean id="ClassificationService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
@@ -76,9 +75,7 @@
|
||||
<property name="objectDefinitionSource">
|
||||
<value>
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService.getClassificationLevels=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService.getCurrentClassification=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService.getClassificationReasons=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService.classifyContent=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService.getClassificationLevelById=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService.getClassificationReasonById=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationService.getUnclassifiedClassificationLevel=ACL_ALLOW
|
||||
@@ -94,9 +91,9 @@
|
||||
<bean id="classificationServiceBootstrap"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceBootstrap">
|
||||
<constructor-arg ref="rm.authenticationUtil"/>
|
||||
<constructor-arg ref="classificationService"/> <!-- Intentionally using the small 'c' here -->
|
||||
<constructor-arg ref="securityClearanceService"/> <!-- Intentionally using the small 's' here -->
|
||||
<constructor-arg ref="TransactionService"/>
|
||||
<constructor-arg ref="attributeService"/>
|
||||
<constructor-arg ref="classificationServiceDAO"/>
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -104,9 +101,9 @@
|
||||
|
||||
<bean id="securityClearanceService"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.classification.SecurityClearanceServiceImpl"
|
||||
parent="baseService">
|
||||
<property name="classificationService" ref="ClassificationService"/> <!-- Intentionally using capital 'C' -->
|
||||
parent="baseService" init-method="init">
|
||||
<property name="personService" ref="PersonService"/>
|
||||
<property name="classificationServiceBootstrap" ref="classificationServiceBootstrap"/>
|
||||
</bean>
|
||||
|
||||
<bean id="SecurityClearanceService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
@@ -140,7 +137,6 @@
|
||||
<bean id="SecurityClearanceService_security" parent="baseSecurity">
|
||||
<property name="objectDefinitionSource">
|
||||
<value>
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.SecurityClearanceService.hasClearance=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.SecurityClearanceService.getUserSecurityClearance=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.SecurityClearanceService.getUsersSecurityClearance=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.SecurityClearanceService.setUserSecurityClearance=ACL_ALLOW
|
||||
@@ -149,4 +145,52 @@
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Content Classification Service -->
|
||||
|
||||
<bean id="contentClassificationService"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationServiceImpl"
|
||||
parent="baseService" init-method="init">
|
||||
<property name="securityClearanceService" ref="SecurityClearanceService"/>
|
||||
<property name="classificationServiceBootstrap" ref="classificationServiceBootstrap"/>
|
||||
</bean>
|
||||
|
||||
<bean id="ContentClassificationService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
<property name="proxyInterfaces">
|
||||
<value>org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService</value>
|
||||
</property>
|
||||
<property name="target">
|
||||
<ref bean="contentClassificationService"/>
|
||||
</property>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<idref local="ContentClassificationService_transaction"/>
|
||||
<idref bean="exceptionTranslator"/>
|
||||
<idref local="ContentClassificationService_security"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="ContentClassificationService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
|
||||
<property name="transactionManager">
|
||||
<ref bean="transactionManager"/>
|
||||
</property>
|
||||
<property name="transactionAttributes">
|
||||
<props>
|
||||
<prop key="*">${server.transaction.mode.default}</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- FIXME: We have to restrict methods in the classification service (with capabilities, etc.) -->
|
||||
<bean id="ContentClassificationService_security" parent="baseSecurity">
|
||||
<property name="objectDefinitionSource">
|
||||
<value>
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService.getCurrentClassification=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService.classifyContent=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService.hasClearance=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService.*=ACL_DENY
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
</beans>
|
||||
|
@@ -674,7 +674,7 @@
|
||||
<bean id="webscript.org.alfresco.rma.classification.classifycontent.post"
|
||||
class="org.alfresco.module.org_alfresco_module_rm.script.classification.ClassifyContentPost"
|
||||
parent="rmBaseWebscript">
|
||||
<property name="classificationService" ref="ClassificationService" />
|
||||
<property name="contentClassificationService" ref="contentClassificationService" />
|
||||
</bean>
|
||||
|
||||
<!-- REST impl for GET user security clearance -->
|
||||
|
@@ -21,9 +21,8 @@ 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.
|
||||
@@ -32,7 +31,7 @@ import com.google.common.collect.ImmutableList;
|
||||
*/
|
||||
public class ClassificationLevelManager
|
||||
{
|
||||
/** Unclassified classificaiton level */
|
||||
/** 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);
|
||||
@@ -41,11 +40,11 @@ public class ClassificationLevelManager
|
||||
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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
@@ -45,35 +41,12 @@ public interface ClassificationService
|
||||
*/
|
||||
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.
|
||||
* @return classification reasons in the order that they are defined.
|
||||
*/
|
||||
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.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,55 +89,6 @@ 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;
|
||||
|
@@ -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.
|
||||
@@ -39,15 +37,13 @@ public class ClearanceLevelManager
|
||||
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,73 +45,21 @@ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements Sec
|
||||
{
|
||||
/** The clearance levels currently configured in this server. */
|
||||
private ClearanceLevelManager clearanceManager;
|
||||
|
||||
private ClassificationService classificationService;
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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;
|
||||
this.classificationLevelManager = classificationServiceBootstrap.getClassificationLevelManager();
|
||||
this.clearanceManager = classificationServiceBootstrap.getClearanceLevelManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -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");
|
||||
|
@@ -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);
|
||||
|
@@ -64,7 +64,7 @@ public class ClassificationLevelsTest extends BaseRMTestCase
|
||||
{
|
||||
List<ClassificationLevel> levels = classificationService.getClassificationLevels();
|
||||
assertNotNull(levels);
|
||||
assertEquals(5, levels.size());
|
||||
assertEquals("Expected 5 levels to be configured (4 defined in the test plus Unclassified)", 5, levels.size());
|
||||
|
||||
ClassificationLevel level1 = levels.get(0);
|
||||
ClassificationLevel level2 = levels.get(1);
|
||||
|
@@ -62,7 +62,7 @@ public class ClassifyTest extends BaseRMTestCase
|
||||
|
||||
public void when() throws Exception
|
||||
{
|
||||
classificationService.classifyContent(
|
||||
contentClassificationService.classifyContent(
|
||||
CLASSIFICATION_LEVEL,
|
||||
CLASSIFICATION_AUTHORITY,
|
||||
Collections.singleton(CLASSIFICATION_REASON),
|
||||
@@ -90,7 +90,7 @@ public class ClassifyTest extends BaseRMTestCase
|
||||
|
||||
public void when() throws Exception
|
||||
{
|
||||
classificationService.classifyContent(
|
||||
contentClassificationService.classifyContent(
|
||||
CLASSIFICATION_LEVEL,
|
||||
CLASSIFICATION_AUTHORITY,
|
||||
Collections.singleton(CLASSIFICATION_REASON),
|
||||
|
@@ -30,6 +30,7 @@ import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditSe
|
||||
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
|
||||
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.dataset.DataSetService;
|
||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
|
||||
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
|
||||
@@ -162,6 +163,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
|
||||
protected InplaceRecordService inplaceRecordService;
|
||||
protected RelationshipService relationshipService;
|
||||
protected ClassificationService classificationService;
|
||||
protected ContentClassificationService contentClassificationService;
|
||||
|
||||
/** test data */
|
||||
protected String siteId;
|
||||
@@ -402,6 +404,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase
|
||||
inplaceRecordService = (InplaceRecordService) applicationContext.getBean("InplaceRecordService");
|
||||
relationshipService = (RelationshipService) applicationContext.getBean("RelationshipService");
|
||||
classificationService = (ClassificationService) applicationContext.getBean("ClassificationService");
|
||||
contentClassificationService = (ContentClassificationService) applicationContext.getBean("ContentClassificationService");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -43,7 +43,8 @@ public class ClassificationLevelManagerUnitTest
|
||||
|
||||
@Before public void setup()
|
||||
{
|
||||
classificationLevelManager = new ClassificationLevelManager(LEVELS);
|
||||
classificationLevelManager = new ClassificationLevelManager();
|
||||
classificationLevelManager.setClassificationLevels(LEVELS);
|
||||
}
|
||||
|
||||
@Test public void findClassificationById_found()
|
||||
|
@@ -43,7 +43,8 @@ public class ClassificationReasonManagerUnitTest
|
||||
|
||||
@Before public void setup()
|
||||
{
|
||||
classificationReasonManager = new ClassificationReasonManager(REASONS);
|
||||
classificationReasonManager = new ClassificationReasonManager();
|
||||
classificationReasonManager.setClassificationReasons(REASONS);
|
||||
}
|
||||
|
||||
@Test public void findClassificationById_found()
|
||||
|
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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 java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.test.util.MockAuthenticationUtilHelper;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
import org.apache.log4j.Appender;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.spi.LoggingEvent;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link ClassificationServiceBootstrap}.
|
||||
*
|
||||
* @author tpage
|
||||
*/
|
||||
public class ClassificationServiceBootstrapUnitTest
|
||||
{
|
||||
private static final List<ClassificationLevel> DEFAULT_CLASSIFICATION_LEVELS = asLevelList("Top Secret", "rm.classification.topSecret",
|
||||
"Secret", "rm.classification.secret",
|
||||
"Confidential", "rm.classification.confidential",
|
||||
"No Clearance", "rm.classification.noClearance");
|
||||
private static final List<ClassificationLevel> ALT_CLASSIFICATION_LEVELS = asLevelList("Board", "B",
|
||||
"Executive Management", "EM",
|
||||
"Employee", "E",
|
||||
"Public", "P");
|
||||
private static final List<ClassificationReason> PLACEHOLDER_CLASSIFICATION_REASONS = asList(new ClassificationReason("id1", "label1"),
|
||||
new ClassificationReason("id2", "label2"));
|
||||
private static final List<ClassificationReason> ALTERNATIVE_CLASSIFICATION_REASONS = asList(new ClassificationReason("id8", "label8"),
|
||||
new ClassificationReason("id9", "label9"));
|
||||
|
||||
/**
|
||||
* A convenience method for turning lists of level id Strings into lists
|
||||
* of {@code ClassificationLevel} objects.
|
||||
*
|
||||
* @param idsAndLabels A varargs/array of Strings like so: [ id0, label0, id1, label1... ]
|
||||
* @throws IllegalArgumentException if {@code idsAndLabels} has a non-even length.
|
||||
*/
|
||||
public static List<ClassificationLevel> asLevelList(String ... idsAndLabels)
|
||||
{
|
||||
if (idsAndLabels.length % 2 != 0)
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Cannot create %s objects with %d args.",
|
||||
ClassificationLevel.class.getSimpleName(), idsAndLabels.length));
|
||||
}
|
||||
|
||||
final List<ClassificationLevel> levels = new ArrayList<>(idsAndLabels.length / 2);
|
||||
|
||||
for (int i = 0; i < idsAndLabels.length; i += 2)
|
||||
{
|
||||
levels.add(new ClassificationLevel(idsAndLabels[i], idsAndLabels[i+1]));
|
||||
}
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
||||
@InjectMocks ClassificationServiceBootstrap classificationServiceBootstrap;
|
||||
@Mock ClassificationServiceDAO mockClassificationServiceDAO;
|
||||
@Mock AttributeService mockAttributeService;
|
||||
@Mock AuthenticationUtil mockAuthenticationUtil;
|
||||
/** Using a mock appender in the class logger so that we can verify some of the logging requirements. */
|
||||
@Mock Appender mockAppender;
|
||||
@Captor ArgumentCaptor<LoggingEvent> loggingEventCaptor;
|
||||
|
||||
@Before public void setUp()
|
||||
{
|
||||
MockitoAnnotations.initMocks(this);
|
||||
MockAuthenticationUtilHelper.setup(mockAuthenticationUtil);
|
||||
}
|
||||
|
||||
@Test public void defaultLevelsConfigurationVanillaSystem()
|
||||
{
|
||||
when(mockClassificationServiceDAO.getConfiguredLevels()).thenReturn(DEFAULT_CLASSIFICATION_LEVELS);
|
||||
when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null);
|
||||
|
||||
classificationServiceBootstrap.initConfiguredClassificationLevels();
|
||||
|
||||
verify(mockAttributeService).setAttribute(eq((Serializable) DEFAULT_CLASSIFICATION_LEVELS),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test public void alternativeLevelsConfigurationPreviouslyStartedSystem()
|
||||
{
|
||||
when(mockClassificationServiceDAO.getConfiguredLevels()).thenReturn(ALT_CLASSIFICATION_LEVELS);
|
||||
when(mockAttributeService.getAttribute(anyString(), anyString(), anyString()))
|
||||
.thenReturn((Serializable) DEFAULT_CLASSIFICATION_LEVELS);
|
||||
|
||||
classificationServiceBootstrap.initConfiguredClassificationLevels();
|
||||
|
||||
verify(mockAttributeService).setAttribute(eq((Serializable) ALT_CLASSIFICATION_LEVELS),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test (expected=MissingConfiguration.class)
|
||||
public void missingLevelsConfigurationVanillaSystemShouldFail() throws Exception
|
||||
{
|
||||
when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null);
|
||||
|
||||
classificationServiceBootstrap.initConfiguredClassificationLevels();
|
||||
}
|
||||
|
||||
@Test public void pristineSystemShouldBootstrapReasonsConfiguration()
|
||||
{
|
||||
// There are no classification reasons stored in the AttributeService.
|
||||
when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null);
|
||||
|
||||
// We'll use a small set of placeholder classification reasons.
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
|
||||
classificationServiceBootstrap.initConfiguredClassificationReasons();
|
||||
|
||||
verify(mockAttributeService).setAttribute(eq((Serializable)PLACEHOLDER_CLASSIFICATION_REASONS),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test public void checkAttributesNotTouchedIfConfiguredReasonsHaveNotChanged()
|
||||
{
|
||||
// The classification reasons stored are the same values that are found on the classpath.
|
||||
when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn((Serializable)PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
|
||||
classificationServiceBootstrap.initConfiguredClassificationReasons();
|
||||
|
||||
verify(mockAttributeService, never()).setAttribute(any(Serializable.class),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that if the reasons supplied on the classpath differ from those already persisted then a warning is logged
|
||||
* and no change is made to the persisted reasons.
|
||||
* <p>
|
||||
* This test uses the underlying log4j implementation to insert a mock Appender, and tests this for the warning
|
||||
* message. If the underlying logging framework is changed then this unit test will fail, and it may not be
|
||||
* possible to/worth fixing.
|
||||
*/
|
||||
@Test public void previouslyStartedSystemShouldWarnIfConfiguredReasonsHaveChanged()
|
||||
{
|
||||
// The classification reasons stored are different from those found on the classpath.
|
||||
when(mockAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(
|
||||
(Serializable) PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(ALTERNATIVE_CLASSIFICATION_REASONS);
|
||||
|
||||
// Put the mock Appender into the log4j logger and allow warning messages to be received.
|
||||
org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger(ClassificationServiceBootstrap.class);
|
||||
log4jLogger.addAppender(mockAppender);
|
||||
Level normalLevel = log4jLogger.getLevel();
|
||||
log4jLogger.setLevel(Level.WARN);
|
||||
|
||||
// Call the method under test.
|
||||
classificationServiceBootstrap.initConfiguredClassificationReasons();
|
||||
|
||||
// Reset the logging level for other tests.
|
||||
log4jLogger.setLevel(normalLevel);
|
||||
|
||||
// Check the persisted values weren't changed.
|
||||
verify(mockAttributeService, never()).setAttribute(any(Serializable.class),
|
||||
anyString(), anyString(), anyString());
|
||||
|
||||
// Check that the warning message was logged.
|
||||
verify(mockAppender).doAppend(loggingEventCaptor.capture());
|
||||
List<LoggingEvent> loggingEvents = loggingEventCaptor.getAllValues();
|
||||
Stream<String> messages = loggingEvents.stream().map(event -> event.getRenderedMessage());
|
||||
String expectedMessage = "Classification reasons configured in classpath do not match those stored in Alfresco. Alfresco will use the unchanged values stored in the database.";
|
||||
assertTrue("Warning message not found in log.", messages.anyMatch(message -> message == expectedMessage));
|
||||
}
|
||||
|
||||
@Test(expected = MissingConfiguration.class)
|
||||
public void noReasonsFoundCausesException()
|
||||
{
|
||||
when(mockAttributeService.getAttribute(anyString(), anyString(), anyString()))
|
||||
.thenReturn((Serializable) null);
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(null);
|
||||
|
||||
classificationServiceBootstrap.initConfiguredClassificationReasons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the initialise method creates a clearance level corresponding to each classification level and that
|
||||
* the display label for the lowest clearance level is "No Clearance" (rather than "Unclassified").
|
||||
*/
|
||||
@Test public void initConfiguredClearanceLevels()
|
||||
{
|
||||
ClassificationLevel topSecret = new ClassificationLevel("1", "TopSecret");
|
||||
ClassificationLevel secret = new ClassificationLevel("2", "Secret");
|
||||
ImmutableList<ClassificationLevel> classificationLevels = ImmutableList.of(topSecret, secret, ClassificationLevelManager.UNCLASSIFIED);
|
||||
|
||||
// Call the method under test.
|
||||
classificationServiceBootstrap.initConfiguredClearanceLevels(classificationLevels);
|
||||
|
||||
List<ClearanceLevel> clearanceLevels = classificationServiceBootstrap.getClearanceLevelManager().getClearanceLevels();
|
||||
assertEquals("There should be one clearance level for each classification level.", classificationLevels.size(), clearanceLevels.size());
|
||||
assertEquals("TopSecret", clearanceLevels.get(0).getDisplayLabel());
|
||||
assertEquals("Secret", clearanceLevels.get(1).getDisplayLabel());
|
||||
assertEquals("rm.classification.noClearance", clearanceLevels.get(2).getDisplayLabel());
|
||||
}
|
||||
}
|
@@ -18,45 +18,21 @@
|
||||
*/
|
||||
package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateNodeRef;
|
||||
import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateText;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.test.util.ExceptionUtils;
|
||||
import org.alfresco.module.org_alfresco_module_rm.test.util.MockAuthenticationUtilHelper;
|
||||
import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil;
|
||||
import org.alfresco.service.cmr.attributes.AttributeService;
|
||||
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;
|
||||
import org.apache.log4j.Appender;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.spi.LoggingEvent;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -65,8 +41,6 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ClassificationServiceImpl}.
|
||||
*
|
||||
@@ -79,17 +53,6 @@ public class ClassificationServiceImplUnitTest
|
||||
"Secret", "rm.classification.secret",
|
||||
"Confidential", "rm.classification.confidential",
|
||||
"No Clearance", "rm.classification.noClearance");
|
||||
private static final List<ClassificationLevel> ALT_CLASSIFICATION_LEVELS = asLevelList("Board", "B",
|
||||
"Executive Management", "EM",
|
||||
"Employee", "E",
|
||||
"Public", "P");
|
||||
private static final List<ClassificationReason> PLACEHOLDER_CLASSIFICATION_REASONS = asList(new ClassificationReason("id1", "label1"),
|
||||
new ClassificationReason("id2", "label2"));
|
||||
private static final List<ClassificationReason> ALTERNATIVE_CLASSIFICATION_REASONS = asList(new ClassificationReason("id8", "label8"),
|
||||
new ClassificationReason("id9", "label9"));
|
||||
|
||||
private static final String CLASSIFICATION_LEVEL_ID = "classificationLevelId";
|
||||
private static final ClassificationLevel CLASSIFICATION_LEVEL = new ClassificationLevel(CLASSIFICATION_LEVEL_ID, generateText());
|
||||
|
||||
/**
|
||||
* A convenience method for turning lists of level id Strings into lists
|
||||
@@ -117,128 +80,15 @@ public class ClassificationServiceImplUnitTest
|
||||
|
||||
@InjectMocks private ClassificationServiceImpl classificationServiceImpl;
|
||||
|
||||
@Mock private AttributeService mockedAttributeService;
|
||||
@Mock private AuthenticationUtil mockedAuthenticationUtil;
|
||||
@Mock private ClassificationServiceDAO mockClassificationServiceDAO;
|
||||
@Mock private NodeService mockNodeService;
|
||||
@Mock private DictionaryService mockDictionaryService;
|
||||
/** Using a mock appender in the class logger so that we can verify some of the logging requirements. */
|
||||
@Mock private Appender mockAppender;
|
||||
@Mock private ClassificationLevelManager mockLevelManager;
|
||||
@Mock private ClassificationReasonManager mockReasonManager;
|
||||
@Captor private ArgumentCaptor<LoggingEvent> loggingEventCaptor;
|
||||
@Captor private ArgumentCaptor<Map<QName, Serializable>> propertiesCaptor;
|
||||
|
||||
@Before public void setUp()
|
||||
{
|
||||
MockitoAnnotations.initMocks(this);
|
||||
MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil);
|
||||
}
|
||||
|
||||
@Test public void defaultLevelsConfigurationVanillaSystem()
|
||||
{
|
||||
when(mockClassificationServiceDAO.getConfiguredLevels()).thenReturn(DEFAULT_CLASSIFICATION_LEVELS);
|
||||
when(mockedAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null);
|
||||
|
||||
classificationServiceImpl.initConfiguredClassificationLevels();
|
||||
|
||||
verify(mockedAttributeService).setAttribute(eq((Serializable) DEFAULT_CLASSIFICATION_LEVELS),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test public void alternativeLevelsConfigurationPreviouslyStartedSystem()
|
||||
{
|
||||
when(mockClassificationServiceDAO.getConfiguredLevels()).thenReturn(ALT_CLASSIFICATION_LEVELS);
|
||||
when(mockedAttributeService.getAttribute(anyString(), anyString(), anyString()))
|
||||
.thenReturn((Serializable) DEFAULT_CLASSIFICATION_LEVELS);
|
||||
|
||||
classificationServiceImpl.initConfiguredClassificationLevels();
|
||||
|
||||
verify(mockedAttributeService).setAttribute(eq((Serializable) ALT_CLASSIFICATION_LEVELS),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test (expected=MissingConfiguration.class)
|
||||
public void missingLevelsConfigurationVanillaSystemShouldFail() throws Exception
|
||||
{
|
||||
when(mockedAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null);
|
||||
|
||||
classificationServiceImpl.initConfiguredClassificationLevels();
|
||||
}
|
||||
|
||||
@Test public void pristineSystemShouldBootstrapReasonsConfiguration()
|
||||
{
|
||||
// There are no classification reasons stored in the AttributeService.
|
||||
when(mockedAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(null);
|
||||
|
||||
// We'll use a small set of placeholder classification reasons.
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
|
||||
classificationServiceImpl.initConfiguredClassificationReasons();
|
||||
|
||||
verify(mockedAttributeService).setAttribute(eq((Serializable)PLACEHOLDER_CLASSIFICATION_REASONS),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test public void checkAttributesNotTouchedIfConfiguredReasonsHaveNotChanged()
|
||||
{
|
||||
// The classification reasons stored are the same values that are found on the classpath.
|
||||
when(mockedAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn((Serializable)PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
|
||||
classificationServiceImpl.initConfiguredClassificationReasons();
|
||||
|
||||
verify(mockedAttributeService, never()).setAttribute(any(Serializable.class),
|
||||
anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that if the reasons supplied on the classpath differ from those already persisted then a warning is logged
|
||||
* and no change is made to the persisted reasons.
|
||||
* <p>
|
||||
* This test uses the underlying log4j implementation to insert a mock Appender, and tests this for the warning
|
||||
* message. If the underlying logging framework is changed then this unit test will fail, and it may not be
|
||||
* possible to/worth fixing.
|
||||
*/
|
||||
@Test public void previouslyStartedSystemShouldWarnIfConfiguredReasonsHaveChanged()
|
||||
{
|
||||
// The classification reasons stored are different from those found on the classpath.
|
||||
when(mockedAttributeService.getAttribute(anyString(), anyString(), anyString())).thenReturn(
|
||||
(Serializable) PLACEHOLDER_CLASSIFICATION_REASONS);
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(ALTERNATIVE_CLASSIFICATION_REASONS);
|
||||
|
||||
// Put the mock Appender into the log4j logger and allow warning messages to be received.
|
||||
org.apache.log4j.Logger log4jLogger = org.apache.log4j.Logger.getLogger(ClassificationServiceImpl.class);
|
||||
log4jLogger.addAppender(mockAppender);
|
||||
Level normalLevel = log4jLogger.getLevel();
|
||||
log4jLogger.setLevel(Level.WARN);
|
||||
|
||||
// Call the method under test.
|
||||
classificationServiceImpl.initConfiguredClassificationReasons();
|
||||
|
||||
// Reset the logging level for other tests.
|
||||
log4jLogger.setLevel(normalLevel);
|
||||
|
||||
// Check the persisted values weren't changed.
|
||||
verify(mockedAttributeService, never()).setAttribute(any(Serializable.class),
|
||||
anyString(), anyString(), anyString());
|
||||
|
||||
// Check that the warning message was logged.
|
||||
verify(mockAppender).doAppend(loggingEventCaptor.capture());
|
||||
List<LoggingEvent> loggingEvents = loggingEventCaptor.getAllValues();
|
||||
Stream<String> messages = loggingEvents.stream().map(event -> event.getRenderedMessage());
|
||||
String expectedMessage = "Classification reasons configured in classpath do not match those stored in Alfresco. Alfresco will use the unchanged values stored in the database.";
|
||||
assertTrue("Warning message not found in log.", messages.anyMatch(message -> message == expectedMessage));
|
||||
}
|
||||
|
||||
@Test(expected = MissingConfiguration.class)
|
||||
public void noReasonsFoundCausesException()
|
||||
{
|
||||
when(mockedAttributeService.getAttribute(anyString(), anyString(), anyString()))
|
||||
.thenReturn((Serializable) null);
|
||||
when(mockClassificationServiceDAO.getConfiguredReasons()).thenReturn(null);
|
||||
|
||||
classificationServiceImpl.initConfiguredClassificationReasons();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,69 +121,6 @@ public class ClassificationServiceImplUnitTest
|
||||
assertEquals("Expected an empty list when the target level is not found.", 0, actual.size());
|
||||
}
|
||||
|
||||
/** Classify a piece of content with a couple of reasons and check the NodeService is called correctly. */
|
||||
@Test public void classifyContent_success()
|
||||
{
|
||||
// Create a level and two reasons.
|
||||
ClassificationLevel level = new ClassificationLevel("levelId1", "displayLabelKey");
|
||||
ClassificationReason reason1 = new ClassificationReason("reasonId1", "displayLabelKey1");
|
||||
ClassificationReason reason2 = new ClassificationReason("reasonId2", "displayLabelKey2");
|
||||
// Set up the managers to return these objects when the ids are provided.
|
||||
when(mockLevelManager.findLevelById("levelId1")).thenReturn(level);
|
||||
when(mockReasonManager.findReasonById("reasonId1")).thenReturn(reason1);
|
||||
when(mockReasonManager.findReasonById("reasonId2")).thenReturn(reason2);
|
||||
// Create a content node.
|
||||
NodeRef content = new NodeRef("fake://content/");
|
||||
when(mockDictionaryService.isSubClass(mockNodeService.getType(content), ContentModel.TYPE_CONTENT)).thenReturn(true);
|
||||
when(mockNodeService.hasAspect(content, ClassifiedContentModel.ASPECT_CLASSIFIED)).thenReturn(false);
|
||||
|
||||
// Call the method under test.
|
||||
classificationServiceImpl.classifyContent("levelId1", "classificationAuthority",
|
||||
Sets.newHashSet("reasonId1", "reasonId2"), content);
|
||||
|
||||
verify(mockNodeService).addAspect(eq(content), eq(ClassifiedContentModel.ASPECT_CLASSIFIED),
|
||||
propertiesCaptor.capture());
|
||||
// Check the properties that were received.
|
||||
Map<QName, Serializable> properties = propertiesCaptor.getValue();
|
||||
HashSet<QName> expectedPropertyKeys = Sets.newHashSet(ClassifiedContentModel.PROP_INITIAL_CLASSIFICATION,
|
||||
ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION,
|
||||
ClassifiedContentModel.PROP_CLASSIFICATION_AUTHORITY,
|
||||
ClassifiedContentModel.PROP_CLASSIFICATION_REASONS);
|
||||
assertEquals("Aspect created with unexpected set of keys.", expectedPropertyKeys, properties.keySet());
|
||||
assertEquals("Unexpected initial classification.", level.getId(), properties.get(ClassifiedContentModel.PROP_INITIAL_CLASSIFICATION));
|
||||
assertEquals("Unexpected current classification.", level.getId(), properties.get(ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION));
|
||||
assertEquals("Unexpected authority.", "classificationAuthority", properties.get(ClassifiedContentModel.PROP_CLASSIFICATION_AUTHORITY));
|
||||
Set<String> expectedReasonIds = Sets.newHashSet("reasonId1", "reasonId2");
|
||||
assertEquals("Unexpected set of reasons.", expectedReasonIds, properties.get(ClassifiedContentModel.PROP_CLASSIFICATION_REASONS));
|
||||
}
|
||||
|
||||
/** Classify a folder using the <code>classifyContent</code> method and check that an exception is raised. */
|
||||
@Test(expected = InvalidNode.class)
|
||||
public void classifyContent_notContent()
|
||||
{
|
||||
// Create a folder node.
|
||||
NodeRef notAPieceOfContent = new NodeRef("not://a/piece/of/content/");
|
||||
when(mockNodeService.getType(notAPieceOfContent)).thenReturn(ContentModel.TYPE_FOLDER);
|
||||
|
||||
// Call the method under test.
|
||||
classificationServiceImpl.classifyContent("levelId1", "classificationAuthority",
|
||||
Sets.newHashSet("reasonId1", "reasonId2"), notAPieceOfContent);
|
||||
}
|
||||
|
||||
/** Classify a piece of content that has already been classified. */
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void classifyContent_alreadyClassified()
|
||||
{
|
||||
// Create a classified piece of content.
|
||||
NodeRef classifiedContent = new NodeRef("classified://content/");
|
||||
when(mockDictionaryService.isSubClass(mockNodeService.getType(classifiedContent), ContentModel.TYPE_CONTENT)).thenReturn(true);
|
||||
when(mockNodeService.hasAspect(classifiedContent, ClassifiedContentModel.ASPECT_CLASSIFIED)).thenReturn(true);
|
||||
|
||||
// Call the method under test.
|
||||
classificationServiceImpl.classifyContent("levelId1", "classificationAuthority",
|
||||
Sets.newHashSet("reasonId1", "reasonId2"), classifiedContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClassificationLevelById()
|
||||
{
|
||||
@@ -369,48 +156,4 @@ public class ClassificationServiceImplUnitTest
|
||||
doThrow(new ReasonIdNotFound("Id not found!")).when(mockReasonManager).findReasonById(classificationReasonId);
|
||||
classificationServiceImpl.getClassificationReasonById(classificationReasonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that a node does not have the classify aspect applied
|
||||
* When I ask for the nodes classification
|
||||
* Then 'Unclassified' is returned
|
||||
*/
|
||||
@Test
|
||||
public void getCurrentClassificationWithoutAspectApplied()
|
||||
{
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED))
|
||||
.thenReturn(false);
|
||||
|
||||
ClassificationLevel classificationLevel = classificationServiceImpl.getCurrentClassification(nodeRef);
|
||||
|
||||
assertEquals(ClassificationLevelManager.UNCLASSIFIED, classificationLevel);
|
||||
verify(mockNodeService).hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED);
|
||||
verifyNoMoreInteractions(mockNodeService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that a node is classified
|
||||
* When I ask for the node classification
|
||||
* Then I get the correct classificationlevel
|
||||
*/
|
||||
@Test
|
||||
public void getCurrentClassification()
|
||||
{
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED))
|
||||
.thenReturn(true);
|
||||
when(mockNodeService.getProperty(nodeRef, ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION))
|
||||
.thenReturn(CLASSIFICATION_LEVEL_ID);
|
||||
when(mockLevelManager.findLevelById(CLASSIFICATION_LEVEL_ID))
|
||||
.thenReturn(CLASSIFICATION_LEVEL);
|
||||
|
||||
ClassificationLevel classificationLevel = classificationServiceImpl.getCurrentClassification(nodeRef);
|
||||
|
||||
assertEquals(CLASSIFICATION_LEVEL, classificationLevel);
|
||||
verify(mockNodeService).hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED);
|
||||
verify(mockNodeService).getProperty(nodeRef, ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION);
|
||||
verify(mockLevelManager).findLevelById(CLASSIFICATION_LEVEL_ID);
|
||||
verifyNoMoreInteractions(mockNodeService, mockLevelManager);
|
||||
}
|
||||
}
|
||||
|
@@ -47,8 +47,11 @@ public class ClearanceLevelManagerUnitTest
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
List<ClearanceLevel> clearanceLevels = ImmutableList.of(TOP_SECRET_CLEARANCE, SECRET_CLEARANCE);
|
||||
clearanceLevelManager = new ClearanceLevelManager(clearanceLevels);
|
||||
List<ClearanceLevel> clearanceLevels = ImmutableList.of(TOP_SECRET_CLEARANCE,
|
||||
SECRET_CLEARANCE,
|
||||
ClearanceLevelManager.NO_CLEARANCE);
|
||||
clearanceLevelManager = new ClearanceLevelManager();
|
||||
clearanceLevelManager.setClearanceLevels(clearanceLevels);
|
||||
}
|
||||
|
||||
/** Check that the secret clearance can be found from the classification level id "S". */
|
||||
@@ -67,14 +70,15 @@ public class ClearanceLevelManagerUnitTest
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that I have created a clearance level manager from a list of clearance levels
|
||||
* Given that I have created a clearance level manager from a list of clearance levels (including the no clearance level)
|
||||
* Then the no clearance level is available
|
||||
*
|
||||
* ...(and not added in the same way as for {@link ClassificationLevelManager#setClassificationLevels(List)}).
|
||||
*/
|
||||
@Test public void noClearanceLevel()
|
||||
{
|
||||
assertEquals(3, clearanceLevelManager.getClearanceLevels().size());
|
||||
assertEquals("The three clearance levels should be available.", 3, clearanceLevelManager.getClearanceLevels().size());
|
||||
ClearanceLevel noClearance = clearanceLevelManager.findLevelByClassificationLevelId(ClassificationLevelManager.UNCLASSIFIED_ID);
|
||||
assertEquals(ClearanceLevelManager.NO_CLEARANCE, noClearance);
|
||||
assertEquals(ClassificationLevelManager.UNCLASSIFIED, noClearance.getHighestClassificationLevel());
|
||||
assertEquals("'No clearance' could not be found.", ClearanceLevelManager.NO_CLEARANCE, noClearance);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.test.util.AlfMock.generateNodeRef;
|
||||
import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.generateText;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
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.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.PersonService.PersonInfo;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ContentClassificationServiceImpl}.
|
||||
*
|
||||
* @author tpage
|
||||
*/
|
||||
public class ContentClassificationServiceImplUnitTest implements ClassifiedContentModel
|
||||
{
|
||||
private static final String CLASSIFICATION_LEVEL_ID = "classificationLevelId";
|
||||
private static final ClassificationLevel CLASSIFICATION_LEVEL = new ClassificationLevel(CLASSIFICATION_LEVEL_ID, generateText());
|
||||
|
||||
@InjectMocks ContentClassificationServiceImpl contentClassificationServiceImpl;
|
||||
@Mock ClassificationLevelManager mockLevelManager;
|
||||
@Mock ClassificationReasonManager mockReasonManager;
|
||||
@Mock NodeService mockNodeService;
|
||||
@Mock DictionaryService mockDictionaryService;
|
||||
@Mock SecurityClearanceService mockSecurityClearanceService;
|
||||
@Captor ArgumentCaptor<Map<QName, Serializable>> propertiesCaptor;
|
||||
|
||||
@Before public void setUp()
|
||||
{
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
/** Classify a piece of content with a couple of reasons and check the NodeService is called correctly. */
|
||||
@Test public void classifyContent_success()
|
||||
{
|
||||
// Create a level and two reasons.
|
||||
ClassificationLevel level = new ClassificationLevel("levelId1", "displayLabelKey");
|
||||
ClassificationReason reason1 = new ClassificationReason("reasonId1", "displayLabelKey1");
|
||||
ClassificationReason reason2 = new ClassificationReason("reasonId2", "displayLabelKey2");
|
||||
// Set up the managers to return these objects when the ids are provided.
|
||||
when(mockLevelManager.findLevelById("levelId1")).thenReturn(level);
|
||||
when(mockReasonManager.findReasonById("reasonId1")).thenReturn(reason1);
|
||||
when(mockReasonManager.findReasonById("reasonId2")).thenReturn(reason2);
|
||||
// Create a content node.
|
||||
NodeRef content = new NodeRef("fake://content/");
|
||||
when(mockDictionaryService.isSubClass(mockNodeService.getType(content), ContentModel.TYPE_CONTENT)).thenReturn(true);
|
||||
when(mockNodeService.hasAspect(content, ClassifiedContentModel.ASPECT_CLASSIFIED)).thenReturn(false);
|
||||
|
||||
// Call the method under test.
|
||||
contentClassificationServiceImpl.classifyContent("levelId1", "classificationAuthority",
|
||||
Sets.newHashSet("reasonId1", "reasonId2"), content);
|
||||
|
||||
verify(mockNodeService).addAspect(eq(content), eq(ClassifiedContentModel.ASPECT_CLASSIFIED),
|
||||
propertiesCaptor.capture());
|
||||
// Check the properties that were received.
|
||||
Map<QName, Serializable> properties = propertiesCaptor.getValue();
|
||||
HashSet<QName> expectedPropertyKeys = Sets.newHashSet(ClassifiedContentModel.PROP_INITIAL_CLASSIFICATION,
|
||||
ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION,
|
||||
ClassifiedContentModel.PROP_CLASSIFICATION_AUTHORITY,
|
||||
ClassifiedContentModel.PROP_CLASSIFICATION_REASONS);
|
||||
assertEquals("Aspect created with unexpected set of keys.", expectedPropertyKeys, properties.keySet());
|
||||
assertEquals("Unexpected initial classification.", level.getId(), properties.get(ClassifiedContentModel.PROP_INITIAL_CLASSIFICATION));
|
||||
assertEquals("Unexpected current classification.", level.getId(), properties.get(ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION));
|
||||
assertEquals("Unexpected authority.", "classificationAuthority", properties.get(ClassifiedContentModel.PROP_CLASSIFICATION_AUTHORITY));
|
||||
Set<String> expectedReasonIds = Sets.newHashSet("reasonId1", "reasonId2");
|
||||
assertEquals("Unexpected set of reasons.", expectedReasonIds, properties.get(ClassifiedContentModel.PROP_CLASSIFICATION_REASONS));
|
||||
}
|
||||
|
||||
/** Classify a folder using the <code>classifyContent</code> method and check that an exception is raised. */
|
||||
@Test(expected = InvalidNode.class)
|
||||
public void classifyContent_notContent()
|
||||
{
|
||||
// Create a folder node.
|
||||
NodeRef notAPieceOfContent = new NodeRef("not://a/piece/of/content/");
|
||||
when(mockNodeService.getType(notAPieceOfContent)).thenReturn(ContentModel.TYPE_FOLDER);
|
||||
|
||||
// Call the method under test.
|
||||
contentClassificationServiceImpl.classifyContent("levelId1", "classificationAuthority",
|
||||
Sets.newHashSet("reasonId1", "reasonId2"), notAPieceOfContent);
|
||||
}
|
||||
|
||||
/** Classify a piece of content that has already been classified. */
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void classifyContent_alreadyClassified()
|
||||
{
|
||||
// Create a classified piece of content.
|
||||
NodeRef classifiedContent = new NodeRef("classified://content/");
|
||||
when(mockDictionaryService.isSubClass(mockNodeService.getType(classifiedContent), ContentModel.TYPE_CONTENT)).thenReturn(true);
|
||||
when(mockNodeService.hasAspect(classifiedContent, ClassifiedContentModel.ASPECT_CLASSIFIED)).thenReturn(true);
|
||||
|
||||
// Call the method under test.
|
||||
contentClassificationServiceImpl.classifyContent("levelId1", "classificationAuthority",
|
||||
Sets.newHashSet("reasonId1", "reasonId2"), classifiedContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that a node does not have the classify aspect applied
|
||||
* When I ask for the nodes classification
|
||||
* Then 'Unclassified' is returned
|
||||
*/
|
||||
@Test
|
||||
public void getCurrentClassificationWithoutAspectApplied()
|
||||
{
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED))
|
||||
.thenReturn(false);
|
||||
|
||||
ClassificationLevel classificationLevel = contentClassificationServiceImpl.getCurrentClassification(nodeRef);
|
||||
|
||||
assertEquals(ClassificationLevelManager.UNCLASSIFIED, classificationLevel);
|
||||
verify(mockNodeService).hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED);
|
||||
verifyNoMoreInteractions(mockNodeService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that a node is classified
|
||||
* When I ask for the node classification
|
||||
* Then I get the correct classificationlevel
|
||||
*/
|
||||
@Test
|
||||
public void getCurrentClassification()
|
||||
{
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED))
|
||||
.thenReturn(true);
|
||||
when(mockNodeService.getProperty(nodeRef, ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION))
|
||||
.thenReturn(CLASSIFICATION_LEVEL_ID);
|
||||
when(mockLevelManager.findLevelById(CLASSIFICATION_LEVEL_ID))
|
||||
.thenReturn(CLASSIFICATION_LEVEL);
|
||||
|
||||
ClassificationLevel classificationLevel = contentClassificationServiceImpl.getCurrentClassification(nodeRef);
|
||||
|
||||
assertEquals(CLASSIFICATION_LEVEL, classificationLevel);
|
||||
verify(mockNodeService).hasAspect(nodeRef, ClassifiedContentModel.ASPECT_CLASSIFIED);
|
||||
verify(mockNodeService).getProperty(nodeRef, ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION);
|
||||
verify(mockLevelManager).findLevelById(CLASSIFICATION_LEVEL_ID);
|
||||
verifyNoMoreInteractions(mockNodeService, mockLevelManager);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given that the node is unclassified
|
||||
* When I ask if the current user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void clearedForUnclassifiedNode()
|
||||
{
|
||||
// Content is unclassified by default.
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
|
||||
assertTrue(contentClassificationServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has no security clearance
|
||||
* When I ask if the current user has clearance
|
||||
* Then false
|
||||
*/
|
||||
@Test public void userWithNoClearanceIsntClearedOnClassifiedNode()
|
||||
{
|
||||
// assign test classification to node
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED)).thenReturn(true);
|
||||
String classificationLevelId = generateText();
|
||||
when(mockNodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION)).thenReturn(classificationLevelId);
|
||||
ClassificationLevel classificationLevel = new ClassificationLevel(classificationLevelId, generateText());
|
||||
when(mockLevelManager.findLevelById(classificationLevelId)).thenReturn(classificationLevel);
|
||||
|
||||
// create user with no clearance
|
||||
SecurityClearance clearance = new SecurityClearance(mock(PersonInfo.class), ClearanceLevelManager.NO_CLEARANCE);
|
||||
when(mockSecurityClearanceService.getUserSecurityClearance()).thenReturn(clearance);
|
||||
|
||||
assertFalse(contentClassificationServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has clearance grater than the classification
|
||||
* When I ask if the user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void classifiedNodeUserClearanceGreater()
|
||||
{
|
||||
// init classification levels
|
||||
ClassificationLevel topSecret = new ClassificationLevel("TopSecret", generateText());
|
||||
String secretId = "Secret";
|
||||
ClassificationLevel secret = new ClassificationLevel(secretId, generateText());
|
||||
ClassificationLevel confidential = new ClassificationLevel("Confidential", generateText());
|
||||
List<ClassificationLevel> classificationLevels = Arrays.asList(topSecret, secret, confidential, ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockLevelManager.getClassificationLevels()).thenReturn(ImmutableList.copyOf(classificationLevels));
|
||||
|
||||
// set nodes classification
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED)).thenReturn(true);
|
||||
when(mockNodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION)).thenReturn(secretId);
|
||||
when(mockLevelManager.findLevelById(secretId)).thenReturn(secret);
|
||||
|
||||
// set users security clearance
|
||||
ClearanceLevel topSecretClearance = new ClearanceLevel(topSecret, "Top Secret");
|
||||
SecurityClearance clearance = new SecurityClearance(mock(PersonInfo.class), topSecretClearance);
|
||||
when(mockSecurityClearanceService.getUserSecurityClearance()).thenReturn(clearance);
|
||||
|
||||
assertTrue(contentClassificationServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has clearance equal to the the classification
|
||||
* When I ask if the user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void classifiedNodeUserClearanceEqual()
|
||||
{
|
||||
// init classification levels
|
||||
ClassificationLevel topSecret = new ClassificationLevel("TopSecret", generateText());
|
||||
String secretId = "Secret";
|
||||
ClassificationLevel secret = new ClassificationLevel(secretId, generateText());
|
||||
ClassificationLevel confidential = new ClassificationLevel("Confidential", generateText());
|
||||
List<ClassificationLevel> classificationLevels = Arrays.asList(topSecret, secret, confidential, ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockLevelManager.getClassificationLevels()).thenReturn(ImmutableList.copyOf(classificationLevels));
|
||||
|
||||
// set nodes classification
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED)).thenReturn(true);
|
||||
when(mockNodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION)).thenReturn(secretId);
|
||||
when(mockLevelManager.findLevelById(secretId)).thenReturn(secret);
|
||||
|
||||
// set users security clearance
|
||||
ClearanceLevel secretClearance = new ClearanceLevel(secret, "Secret");
|
||||
SecurityClearance clearance = new SecurityClearance(mock(PersonInfo.class), secretClearance);
|
||||
when(mockSecurityClearanceService.getUserSecurityClearance()).thenReturn(clearance);
|
||||
|
||||
assertTrue(contentClassificationServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has clearance less than the classification
|
||||
* When I ask if the user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void classifiedNodeUserClearanceLess()
|
||||
{
|
||||
// init classification levels
|
||||
ClassificationLevel topSecret = new ClassificationLevel("TopSecret", generateText());
|
||||
String secretId = "Secret";
|
||||
ClassificationLevel secret = new ClassificationLevel(secretId, generateText());
|
||||
ClassificationLevel confidential = new ClassificationLevel("Confidential", generateText());
|
||||
List<ClassificationLevel> classificationLevels = Arrays.asList(topSecret, secret, confidential, ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockLevelManager.getClassificationLevels()).thenReturn(ImmutableList.copyOf(classificationLevels));
|
||||
|
||||
// set nodes classification
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockNodeService.hasAspect(nodeRef, ASPECT_CLASSIFIED)).thenReturn(true);
|
||||
when(mockNodeService.getProperty(nodeRef, PROP_CURRENT_CLASSIFICATION)).thenReturn(secretId);
|
||||
when(mockLevelManager.findLevelById(secretId)).thenReturn(secret);
|
||||
|
||||
// set users security clearance
|
||||
ClearanceLevel confidentialClearance = new ClearanceLevel(confidential, "Confidential");
|
||||
SecurityClearance clearance = new SecurityClearance(mock(PersonInfo.class), confidentialClearance);
|
||||
when(mockSecurityClearanceService.getUserSecurityClearance()).thenReturn(clearance);
|
||||
|
||||
assertFalse(contentClassificationServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
}
|
@@ -20,14 +20,15 @@ package org.alfresco.module.org_alfresco_module_rm.classification;
|
||||
|
||||
import static org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel.ASPECT_SECURITY_CLEARANCE;
|
||||
import static org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel.PROP_CLEARANCE_LEVEL;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.alfresco.module.org_alfresco_module_rm.test.util.AlfMock.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -58,11 +59,12 @@ public class SecurityClearanceServiceImplUnitTest
|
||||
{
|
||||
@InjectMocks private SecurityClearanceServiceImpl securityClearanceServiceImpl;
|
||||
|
||||
@Mock private AuthenticationUtil mockedAuthenticationUtil;
|
||||
@Mock private ClassificationService mockClassificationService;
|
||||
@Mock private AuthenticationUtil mockAuthenticationUtil;
|
||||
@Mock private ClassificationLevelManager mockClassificationLevelManager;
|
||||
@Mock private DictionaryService mockDictionaryService;
|
||||
@Mock private NodeService mockNodeService;
|
||||
@Mock private PersonService mockPersonService;
|
||||
@Mock private ClassificationService mockClassificationService;
|
||||
@Mock private ClearanceLevelManager mockClearanceLevelManager;
|
||||
|
||||
@Before public void setUp()
|
||||
@@ -76,15 +78,15 @@ public class SecurityClearanceServiceImplUnitTest
|
||||
final PersonInfo info = new PersonInfo(userNode, userName, firstName, lastName);
|
||||
|
||||
when(mockPersonService.getPerson(eq(userName), anyBoolean())).thenReturn(userNode);
|
||||
when(mockPersonService.getPerson(eq(userNode))).thenReturn(info);
|
||||
when(mockPersonService.getPerson(userNode)).thenReturn(info);
|
||||
|
||||
when(mockNodeService.hasAspect(eq(userNode), eq(ASPECT_SECURITY_CLEARANCE))).thenReturn(clearanceLevel != null);
|
||||
when(mockNodeService.getProperty(eq(userNode), eq(PROP_CLEARANCE_LEVEL))).thenReturn(clearanceLevel);
|
||||
when(mockNodeService.hasAspect(userNode, ASPECT_SECURITY_CLEARANCE)).thenReturn(clearanceLevel != null);
|
||||
when(mockNodeService.getProperty(userNode, PROP_CLEARANCE_LEVEL)).thenReturn(clearanceLevel);
|
||||
|
||||
if (clearanceLevel != null)
|
||||
{
|
||||
final ClassificationLevel dummyValue = new ClassificationLevel(clearanceLevel, clearanceLevel);
|
||||
when(mockClassificationService.getClassificationLevelById(eq(clearanceLevel))).thenReturn(dummyValue);
|
||||
when(mockClassificationLevelManager.findLevelById(clearanceLevel)).thenReturn(dummyValue);
|
||||
}
|
||||
|
||||
return info;
|
||||
@@ -93,8 +95,7 @@ public class SecurityClearanceServiceImplUnitTest
|
||||
@Test public void userWithNoClearanceGetsDefaultClearance()
|
||||
{
|
||||
final PersonInfo user1 = createMockPerson("user1", "User", "One", null);
|
||||
MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil, user1.getUserName());
|
||||
|
||||
MockAuthenticationUtilHelper.setup(mockAuthenticationUtil, user1.getUserName());
|
||||
when(mockClassificationService.getUnclassifiedClassificationLevel())
|
||||
.thenReturn(ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(ClassificationLevelManager.UNCLASSIFIED_ID))
|
||||
@@ -106,233 +107,103 @@ public class SecurityClearanceServiceImplUnitTest
|
||||
|
||||
}
|
||||
|
||||
/** Check that a user can have their clearance set. */
|
||||
/** Check that a user can have their clearance set by an authorised user. */
|
||||
@Test public void setUserSecurityClearance_setClearance()
|
||||
{
|
||||
// Create the user.
|
||||
// Create the clearance.
|
||||
String topSecretId = "ClearanceId";
|
||||
ClassificationLevel level = new ClassificationLevel(topSecretId, "TopSecretKey");
|
||||
ClearanceLevel clearanceLevel = new ClearanceLevel(level, "TopSecretKey");
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(topSecretId)).thenReturn(clearanceLevel);
|
||||
when(mockClassificationLevelManager.getClassificationLevels()).thenReturn(ImmutableList.of(level));
|
||||
|
||||
// Create the authorised user.
|
||||
String authorisedUserName = "authorisedUser";
|
||||
when(mockAuthenticationUtil.getFullyAuthenticatedUser()).thenReturn(authorisedUserName);
|
||||
NodeRef authorisedPersonNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, authorisedUserName);
|
||||
PersonInfo authorisedPersonInfo = new PersonInfo(authorisedPersonNode, authorisedUserName, "first", "last");
|
||||
when(mockPersonService.getPerson(authorisedUserName, false)).thenReturn(authorisedPersonNode);
|
||||
when(mockPersonService.getPerson(authorisedPersonNode)).thenReturn(authorisedPersonInfo);
|
||||
|
||||
// The authorised user is cleared to use this clearance.
|
||||
when(mockNodeService.hasAspect(authorisedPersonNode, ASPECT_SECURITY_CLEARANCE)).thenReturn(true);
|
||||
when(mockNodeService.getProperty(authorisedPersonNode, PROP_CLEARANCE_LEVEL)).thenReturn(topSecretId);
|
||||
|
||||
// Create the user who will have their clearance set.
|
||||
String userName = "User 1";
|
||||
|
||||
NodeRef personNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, userName);
|
||||
PersonInfo personInfo = new PersonInfo(personNode, userName, "user", "two");
|
||||
|
||||
PersonInfo personInfo = new PersonInfo(personNode, userName, "first", "last");
|
||||
when(mockPersonService.getPerson(userName, false)).thenReturn(personNode);
|
||||
when(mockPersonService.getPerson(personNode)).thenReturn(personInfo);
|
||||
|
||||
// Create the clearance.
|
||||
String clearanceId = "ClearanceId";
|
||||
ClassificationLevel level = new ClassificationLevel(clearanceId, "TopSecretKey");
|
||||
when(mockClassificationService.getClassificationLevelById(clearanceId)).thenReturn(level);
|
||||
ClearanceLevel clearanceLevel = new ClearanceLevel(level, "TopSecretKey");
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(clearanceId)).thenReturn(clearanceLevel);
|
||||
|
||||
// Once the user's clearance has been set then the node service is queried about it.
|
||||
when(mockNodeService.hasAspect(personNode, ASPECT_SECURITY_CLEARANCE)).thenReturn(true);
|
||||
when(mockNodeService.getProperty(personNode, PROP_CLEARANCE_LEVEL)).thenReturn(clearanceId);
|
||||
|
||||
when(mockNodeService.getProperty(personNode, PROP_CLEARANCE_LEVEL)).thenReturn(topSecretId);
|
||||
|
||||
// Call the method under test.
|
||||
SecurityClearance securityClearance = securityClearanceServiceImpl.setUserSecurityClearance(userName, clearanceId);
|
||||
SecurityClearance securityClearance = securityClearanceServiceImpl.setUserSecurityClearance(userName, topSecretId);
|
||||
|
||||
// Check the returned value.
|
||||
assertEquals(personInfo, securityClearance.getPersonInfo());
|
||||
assertEquals(clearanceLevel, securityClearance.getClearanceLevel());
|
||||
|
||||
verify(mockNodeService).setProperty(personNode, PROP_CLEARANCE_LEVEL, clearanceId);
|
||||
// Check the value stored in the node service.
|
||||
verify(mockNodeService).setProperty(personNode, PROP_CLEARANCE_LEVEL, topSecretId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a user cannot raise someone's clearance above their own. Here we check that an exception thrown by the
|
||||
* classification service is passed through.
|
||||
*/
|
||||
/** Check that a user cannot raise someone else's clearance above their own. */
|
||||
@Test(expected = LevelIdNotFound.class)
|
||||
public void setUserSecurityClearance_insufficientClearance()
|
||||
{
|
||||
String userName = "User 1";
|
||||
NodeRef personNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, userName);
|
||||
when(mockPersonService.getPerson(userName, false)).thenReturn(personNode);
|
||||
String clearanceId = "ClearanceId";
|
||||
// If the user has insufficient clearance then they cannot access the level.
|
||||
when(mockClassificationService.getClassificationLevelById(clearanceId)).thenThrow(new LevelIdNotFound(clearanceId));
|
||||
// Create the "Top Secret" and "Confidential" clearances.
|
||||
String topSecretId = "TopSecretClearanceId";
|
||||
ClassificationLevel topSecret = new ClassificationLevel(topSecretId, "TopSecretKey");
|
||||
ClearanceLevel topSecretClearance = new ClearanceLevel(topSecret, "TopSecretKey");
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(topSecretId)).thenReturn(topSecretClearance);
|
||||
String confidentialId = "ConfidentialClearanceId";
|
||||
ClassificationLevel confidential = new ClassificationLevel(confidentialId, "ConfidentialKey");
|
||||
ClearanceLevel confidentialClearance = new ClearanceLevel(confidential, "ConfidentialKey");
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(confidentialId)).thenReturn(confidentialClearance);
|
||||
when(mockClassificationLevelManager.getClassificationLevels()).thenReturn(ImmutableList.of(topSecret, confidential));
|
||||
|
||||
securityClearanceServiceImpl.setUserSecurityClearance(userName, clearanceId);
|
||||
// Create the user attempting to use the API with "Confidential" clearance.
|
||||
String userName = "unauthorisedUser";
|
||||
when(mockAuthenticationUtil.getFullyAuthenticatedUser()).thenReturn(userName);
|
||||
NodeRef personNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, userName);
|
||||
PersonInfo personInfo = new PersonInfo(personNode, userName, "first", "last");
|
||||
when(mockPersonService.getPerson(userName, false)).thenReturn(personNode);
|
||||
when(mockPersonService.getPerson(personNode)).thenReturn(personInfo);
|
||||
|
||||
// The authorised user is cleared to use this clearance.
|
||||
when(mockNodeService.hasAspect(personNode, ASPECT_SECURITY_CLEARANCE)).thenReturn(true);
|
||||
when(mockNodeService.getProperty(personNode, PROP_CLEARANCE_LEVEL)).thenReturn(confidentialId);
|
||||
|
||||
// Create the user who will have their clearance set.
|
||||
String targetUserName = "Target User";
|
||||
NodeRef targetPersonNode = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, targetUserName);
|
||||
PersonInfo targetPersonInfo = new PersonInfo(targetPersonNode, targetUserName, "first", "last");
|
||||
when(mockPersonService.getPerson(targetUserName, false)).thenReturn(targetPersonNode);
|
||||
when(mockPersonService.getPerson(targetPersonNode)).thenReturn(targetPersonInfo);
|
||||
|
||||
// Call the method under test and expect an exception.
|
||||
securityClearanceServiceImpl.setUserSecurityClearance(targetUserName, topSecretId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the initialise method creates a clearance level corresponding to each classification level and that
|
||||
* the display label for the lowest clearance level is "No Clearance" (rather than "Unclassified").
|
||||
* Check that a user with no clearance is not cleared to use the "Secret" classification.
|
||||
*/
|
||||
@Test public void initialise()
|
||||
@Test public void isClearedForClassification()
|
||||
{
|
||||
ClassificationLevel topSecret = new ClassificationLevel("1", "TopSecret");
|
||||
ClassificationLevel secret = new ClassificationLevel("2", "Secret");
|
||||
List<ClassificationLevel> classificationLevels = Arrays.asList(topSecret, secret, ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockClassificationService.getClassificationLevels()).thenReturn(classificationLevels );
|
||||
ImmutableList<ClassificationLevel> classificationLevels = ImmutableList.of(topSecret, secret);
|
||||
when(mockClassificationLevelManager.getClassificationLevels()).thenReturn(classificationLevels);
|
||||
|
||||
SecurityClearance clearance = new SecurityClearance(mock(PersonInfo.class), ClearanceLevelManager.NO_CLEARANCE);
|
||||
|
||||
// Call the method under test.
|
||||
securityClearanceServiceImpl.initialise();
|
||||
boolean result = securityClearanceServiceImpl.isClearedForClassification(clearance, "2");
|
||||
|
||||
List<ClearanceLevel> clearanceLevels = securityClearanceServiceImpl.getClearanceManager().getClearanceLevels();
|
||||
assertEquals("There should be one clearance level for each classification level.", classificationLevels.size(), clearanceLevels.size());
|
||||
assertEquals("TopSecret", clearanceLevels.get(0).getDisplayLabel());
|
||||
assertEquals("Secret", clearanceLevels.get(1).getDisplayLabel());
|
||||
assertEquals("rm.classification.noClearance", clearanceLevels.get(2).getDisplayLabel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is unclassified
|
||||
* When I ask if the current user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void clearedForUnclassifiedNode()
|
||||
{
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockClassificationService.getCurrentClassification(nodeRef))
|
||||
.thenReturn(ClassificationLevelManager.UNCLASSIFIED);
|
||||
|
||||
assertTrue(securityClearanceServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has no security clearance
|
||||
* When I ask if the current user has clearance
|
||||
* Then false
|
||||
*/
|
||||
@Test public void userWithNoClearanceIsntClearedOnClassifiedNode()
|
||||
{
|
||||
// assign test classification to node
|
||||
String classificationLevelId = generateText();
|
||||
ClassificationLevel classificationLevel = new ClassificationLevel(classificationLevelId, generateText());
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockClassificationService.getCurrentClassification(nodeRef))
|
||||
.thenReturn(classificationLevel);
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(classificationLevelId))
|
||||
.thenReturn(new ClearanceLevel(classificationLevel, generateText()));
|
||||
|
||||
// create user with no clearance
|
||||
final PersonInfo user1 = createMockPerson(generateText(), generateText(), generateText(), null);
|
||||
MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil, user1.getUserName());
|
||||
when(mockClassificationService.getUnclassifiedClassificationLevel())
|
||||
.thenReturn(ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(ClassificationLevelManager.UNCLASSIFIED_ID))
|
||||
.thenReturn(ClearanceLevelManager.NO_CLEARANCE);
|
||||
|
||||
assertFalse(securityClearanceServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has clearance grater than the classification
|
||||
* When I ask if the user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void classifiedNodeUserClearanceGreater()
|
||||
{
|
||||
// init classification levels
|
||||
ClassificationLevel topSecret = new ClassificationLevel("TopSecret", generateText());
|
||||
ClassificationLevel secret = new ClassificationLevel("Secret", generateText());
|
||||
ClassificationLevel confidential = new ClassificationLevel("Confidential", generateText());
|
||||
List<ClassificationLevel> classificationLevels = Arrays.asList(topSecret, secret, confidential, ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockClassificationService.getClassificationLevels()).thenReturn(classificationLevels);
|
||||
|
||||
// init classification levels
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("TopSecret"))
|
||||
.thenReturn(new ClearanceLevel(topSecret, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("Secret"))
|
||||
.thenReturn(new ClearanceLevel(secret, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("Confidential"))
|
||||
.thenReturn(new ClearanceLevel(confidential, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(ClassificationLevelManager.UNCLASSIFIED_ID))
|
||||
.thenReturn(ClearanceLevelManager.NO_CLEARANCE);
|
||||
when(mockClassificationService.getUnclassifiedClassificationLevel())
|
||||
.thenReturn(ClassificationLevelManager.UNCLASSIFIED);
|
||||
|
||||
// set nodes classification
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockClassificationService.getCurrentClassification(nodeRef))
|
||||
.thenReturn(secret);
|
||||
|
||||
// set users security clearance
|
||||
final PersonInfo user1 = createMockPerson(generateText(), generateText(), generateText(), "TopSecret");
|
||||
MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil, user1.getUserName());
|
||||
|
||||
assertTrue(securityClearanceServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has clearance equal to the the classification
|
||||
* When I ask if the user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void classifiedNodeUserClearanceEqual()
|
||||
{
|
||||
// init classification levels
|
||||
ClassificationLevel topSecret = new ClassificationLevel("TopSecret", generateText());
|
||||
ClassificationLevel secret = new ClassificationLevel("Secret", generateText());
|
||||
ClassificationLevel confidential = new ClassificationLevel("Confidential", generateText());
|
||||
List<ClassificationLevel> classificationLevels = Arrays.asList(topSecret, secret, confidential, ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockClassificationService.getClassificationLevels()).thenReturn(classificationLevels);
|
||||
|
||||
// init classification levels
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("TopSecret"))
|
||||
.thenReturn(new ClearanceLevel(topSecret, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("Secret"))
|
||||
.thenReturn(new ClearanceLevel(secret, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("Confidential"))
|
||||
.thenReturn(new ClearanceLevel(confidential, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(ClassificationLevelManager.UNCLASSIFIED_ID))
|
||||
.thenReturn(ClearanceLevelManager.NO_CLEARANCE);
|
||||
when(mockClassificationService.getUnclassifiedClassificationLevel())
|
||||
.thenReturn(ClassificationLevelManager.UNCLASSIFIED);
|
||||
|
||||
// set nodes classification
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockClassificationService.getCurrentClassification(nodeRef))
|
||||
.thenReturn(secret);
|
||||
|
||||
// set users security clearance
|
||||
final PersonInfo user1 = createMockPerson(generateText(), generateText(), generateText(), "Secret");
|
||||
MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil, user1.getUserName());
|
||||
|
||||
assertTrue(securityClearanceServiceImpl.hasClearance(nodeRef));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the node is classified
|
||||
* And the user has clearance less than the classification
|
||||
* When I ask if the user has clearance
|
||||
* Then true
|
||||
*/
|
||||
@Test public void classifiedNodeUserClearanceLess()
|
||||
{
|
||||
// init classification levels
|
||||
ClassificationLevel topSecret = new ClassificationLevel("TopSecret", generateText());
|
||||
ClassificationLevel secret = new ClassificationLevel("Secret", generateText());
|
||||
ClassificationLevel confidential = new ClassificationLevel("Confidential", generateText());
|
||||
List<ClassificationLevel> classificationLevels = Arrays.asList(topSecret, secret, confidential, ClassificationLevelManager.UNCLASSIFIED);
|
||||
when(mockClassificationService.getClassificationLevels()).thenReturn(classificationLevels);
|
||||
|
||||
// init classification levels
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("TopSecret"))
|
||||
.thenReturn(new ClearanceLevel(topSecret, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("Secret"))
|
||||
.thenReturn(new ClearanceLevel(secret, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId("Confidential"))
|
||||
.thenReturn(new ClearanceLevel(confidential, generateText()));
|
||||
when(mockClearanceLevelManager.findLevelByClassificationLevelId(ClassificationLevelManager.UNCLASSIFIED_ID))
|
||||
.thenReturn(ClearanceLevelManager.NO_CLEARANCE);
|
||||
when(mockClassificationService.getUnclassifiedClassificationLevel())
|
||||
.thenReturn(ClassificationLevelManager.UNCLASSIFIED);
|
||||
|
||||
// set nodes classification
|
||||
NodeRef nodeRef = generateNodeRef(mockNodeService);
|
||||
when(mockClassificationService.getCurrentClassification(nodeRef))
|
||||
.thenReturn(secret);
|
||||
|
||||
// set users security clearance
|
||||
final PersonInfo user1 = createMockPerson(generateText(), generateText(), generateText(), "Confidential");
|
||||
MockAuthenticationUtilHelper.setup(mockedAuthenticationUtil, user1.getUserName());
|
||||
|
||||
assertFalse(securityClearanceServiceImpl.hasClearance(nodeRef));
|
||||
assertFalse("A user with no clearance should not be able to access the 'Secret' classification.", result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -34,7 +34,7 @@ import java.util.Map;
|
||||
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationLevelManager;
|
||||
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationReasonManager;
|
||||
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.test.util.BaseWebScriptUnitTest;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
@@ -68,8 +68,8 @@ public class ClassifyContentPostUnitTest extends BaseWebScriptUnitTest
|
||||
/** ClassifyContentPost webscript instance */
|
||||
private @Spy @InjectMocks ClassifyContentPost webScript;
|
||||
|
||||
/** Mocked classification service */
|
||||
private @Mock ClassificationService mockedClassificationService;
|
||||
/** Mocked content classification service */
|
||||
private @Mock ContentClassificationService mockedContentClassificationService;
|
||||
|
||||
/** Mocked classification level manager */
|
||||
private @Mock ClassificationLevelManager mockedClassificationLevelManager;
|
||||
@@ -116,7 +116,7 @@ public class ClassifyContentPostUnitTest extends BaseWebScriptUnitTest
|
||||
assertEquals(getStringValueFromJSONObject(json, SUCCESS), Boolean.TRUE.toString());
|
||||
|
||||
// Verify that the classify content method was called
|
||||
verify(mockedClassificationService, times(1)).classifyContent(LEVEL_ID, AUTHORITY, newHashSet(REASON1_ID, REASON2_ID), record);
|
||||
verify(mockedContentClassificationService, times(1)).classifyContent(LEVEL_ID, AUTHORITY, newHashSet(REASON1_ID, REASON2_ID), record);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user