mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
RM-2502 Create new constraint for initial classification.
Previously we were restricting the initial classification to be a level that the current user could access, but this is not always the case for downgraded content. Also add new integration test for initial classification constraint. I tried adding a test that extended our BaseRMTestCase, but the transactions all happened as system (or admin maybe?), and so the level 2 user is always allowed to reclassify level 1 content (by the constraint). Consequently I ended up creating a stand-alone test for this. +review RM git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@110144 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -108,6 +108,7 @@
|
|||||||
<property name="objectDefinitionSource">
|
<property name="objectDefinitionSource">
|
||||||
<value>
|
<value>
|
||||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationLevels=ACL_ALLOW
|
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationLevels=ACL_ALLOW
|
||||||
|
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getAllClassificationLevels=ACL_ALLOW
|
||||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationReasons=ACL_ALLOW
|
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationReasons=ACL_ALLOW
|
||||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationLevelById=ACL_ALLOW
|
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationLevelById=ACL_ALLOW
|
||||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationReasonById=ACL_ALLOW
|
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationReasonById=ACL_ALLOW
|
||||||
|
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
<!-- Model Constraints -->
|
<!-- Model Constraints -->
|
||||||
<constraints>
|
<constraints>
|
||||||
|
<constraint name="clf:initialClassificationLevelRestriction"
|
||||||
|
type="org.alfresco.module.org_alfresco_module_rm.classification.InitialClassificationLevelConstraint" />
|
||||||
<constraint name="clf:classificationLevelRestriction"
|
<constraint name="clf:classificationLevelRestriction"
|
||||||
type="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationLevelConstraint" />
|
type="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationLevelConstraint" />
|
||||||
<constraint name="clf:classificationReasonRestriction"
|
<constraint name="clf:classificationReasonRestriction"
|
||||||
@@ -53,7 +55,7 @@
|
|||||||
<protected>true</protected>
|
<protected>true</protected>
|
||||||
<mandatory>true</mandatory>
|
<mandatory>true</mandatory>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint ref="clf:classificationLevelRestriction" />
|
<constraint ref="clf:initialClassificationLevelRestriction" />
|
||||||
</constraints>
|
</constraints>
|
||||||
</property>
|
</property>
|
||||||
<property name="clf:currentClassification">
|
<property name="clf:currentClassification">
|
||||||
|
@@ -44,6 +44,15 @@ public interface ClassificationSchemeService
|
|||||||
*/
|
*/
|
||||||
List<ClassificationLevel> getClassificationLevels();
|
List<ClassificationLevel> getClassificationLevels();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an immutable list of all the classification levels defined in the system.
|
||||||
|
*
|
||||||
|
* @return classification levels in descending order from highest to lowest
|
||||||
|
* (where fewer users have access to the highest classification levels
|
||||||
|
* and therefore access to the most restricted documents).
|
||||||
|
*/
|
||||||
|
List<ClassificationLevel> getAllClassificationLevels();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an immutable list of the defined classification reasons.
|
* Returns an immutable list of the defined classification reasons.
|
||||||
* @return classification reasons in the order that they are defined.
|
* @return classification reasons in the order that they are defined.
|
||||||
|
@@ -90,6 +90,16 @@ public class ClassificationSchemeServiceImpl extends ServiceBaseImpl implements
|
|||||||
return restrictList(levelManager.getClassificationLevels(), usersLevel);
|
return restrictList(levelManager.getClassificationLevels(), usersLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ClassificationLevel> getAllClassificationLevels()
|
||||||
|
{
|
||||||
|
if (levelManager == null)
|
||||||
|
{
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return levelManager.getClassificationLevels();
|
||||||
|
}
|
||||||
|
|
||||||
@Override public List<ClassificationReason> getClassificationReasons()
|
@Override public List<ClassificationReason> getClassificationReasons()
|
||||||
{
|
{
|
||||||
return reasonManager == null ? Collections.<ClassificationReason>emptyList() :
|
return reasonManager == null ? Collections.<ClassificationReason>emptyList() :
|
||||||
|
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that a value is a valid initial {@link ClassificationLevel} by checking the {@link ClassificationSchemeService}.
|
||||||
|
* The initial classification level is allowed to be any level, regardless of the clearance of the current user.
|
||||||
|
*
|
||||||
|
* @author tpage
|
||||||
|
* @since 3.0.a
|
||||||
|
*/
|
||||||
|
public class InitialClassificationLevelConstraint extends ClassificationSchemeEntityConstraint
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the allowed values. Note that these are <tt>String</tt> instances, but may
|
||||||
|
* represent non-<tt>String</tt> values. It is up to the caller to distinguish.
|
||||||
|
*
|
||||||
|
* @return the values allowed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected List<String> getAllowedValues()
|
||||||
|
{
|
||||||
|
List<ClassificationLevel> classificationLevels = classificationSchemeService.getAllClassificationLevels();
|
||||||
|
List<String> values = new ArrayList<String>();
|
||||||
|
for (ClassificationLevel classificationLevel : classificationLevels)
|
||||||
|
{
|
||||||
|
values.add(classificationLevel.getId());
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* 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.test.integration.classification;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationAspectProperties;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.classification.SecurityClearanceService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.classification.model.ClassifiedContentModel;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.model.rma.type.RmSiteType;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
|
import org.alfresco.repo.security.authority.AuthorityDAO;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.security.MutableAuthenticationService;
|
||||||
|
import org.alfresco.service.cmr.security.PersonService;
|
||||||
|
import org.alfresco.service.cmr.site.SiteService;
|
||||||
|
import org.alfresco.service.cmr.site.SiteVisibility;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.alfresco.util.GUID;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests that require the constraints to be executed by a user other than Admin.
|
||||||
|
*
|
||||||
|
* @author Tom Page
|
||||||
|
* @since 3.0.a
|
||||||
|
*/
|
||||||
|
public class ClassificationConstraintTest implements RMPermissionModel
|
||||||
|
{
|
||||||
|
/* test data */
|
||||||
|
private static final String CLASSIFICATION_REASON = "Test Reason 1";
|
||||||
|
private static final String CLASSIFIED_BY = "classified by text";
|
||||||
|
private static final String RECORD_NAME = "recordname.txt";
|
||||||
|
|
||||||
|
/* Application context */
|
||||||
|
protected String[] getConfigLocations()
|
||||||
|
{
|
||||||
|
return new String[]
|
||||||
|
{
|
||||||
|
"classpath:alfresco/application-context.xml",
|
||||||
|
"classpath:test-context.xml"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
/** Common test utils */
|
||||||
|
private CommonRMTestUtils utils;
|
||||||
|
|
||||||
|
/* Services */
|
||||||
|
private NodeService nodeService;
|
||||||
|
private MutableAuthenticationService authenticationService;
|
||||||
|
private SiteService siteService;
|
||||||
|
private PersonService personService;
|
||||||
|
|
||||||
|
/* RM Services */
|
||||||
|
private FilePlanRoleService filePlanRoleService;
|
||||||
|
private FilePlanPermissionService filePlanPermissionService;
|
||||||
|
private FilePlanService filePlanService;
|
||||||
|
private RecordFolderService recordFolderService;
|
||||||
|
private SecurityClearanceService securityClearanceService;
|
||||||
|
private ContentClassificationService contentClassificationService;
|
||||||
|
|
||||||
|
/* test data */
|
||||||
|
private String siteId;
|
||||||
|
private NodeRef filePlan;
|
||||||
|
private NodeRef rmContainer;
|
||||||
|
private NodeRef rmFolder;
|
||||||
|
|
||||||
|
/** Load the application context and create the test data. */
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception
|
||||||
|
{
|
||||||
|
// Get the application context
|
||||||
|
applicationContext = ApplicationContextHelper.getApplicationContext(getConfigLocations());
|
||||||
|
utils = new CommonRMTestUtils(applicationContext);
|
||||||
|
|
||||||
|
// Initialise the service beans
|
||||||
|
initServices();
|
||||||
|
|
||||||
|
// Setup test data
|
||||||
|
setupTestData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initialise the service beans. */
|
||||||
|
protected void initServices()
|
||||||
|
{
|
||||||
|
// Get services
|
||||||
|
nodeService = (NodeService)applicationContext.getBean("NodeService");
|
||||||
|
siteService = (SiteService)this.applicationContext.getBean("SiteService");
|
||||||
|
authenticationService = (MutableAuthenticationService)this.applicationContext.getBean("AuthenticationService");
|
||||||
|
personService = (PersonService)this.applicationContext.getBean("PersonService");
|
||||||
|
|
||||||
|
// Get RM services
|
||||||
|
filePlanRoleService = (FilePlanRoleService)this.applicationContext.getBean("FilePlanRoleService");
|
||||||
|
filePlanPermissionService = (FilePlanPermissionService)this.applicationContext.getBean("FilePlanPermissionService");
|
||||||
|
filePlanService = (FilePlanService) applicationContext.getBean("FilePlanService");
|
||||||
|
recordFolderService = (RecordFolderService) applicationContext.getBean("RecordFolderService");
|
||||||
|
securityClearanceService = (SecurityClearanceService) applicationContext.getBean("SecurityClearanceService");
|
||||||
|
contentClassificationService = (ContentClassificationService) applicationContext.getBean("ContentClassificationService");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Setup test data for tests */
|
||||||
|
protected void setupTestData()
|
||||||
|
{
|
||||||
|
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
|
||||||
|
{
|
||||||
|
public Void doWork()
|
||||||
|
{
|
||||||
|
AuthorityDAO authDao = (AuthorityDAO)applicationContext.getBean("authorityDAO");
|
||||||
|
if (!authDao.authorityExists(AuthenticationUtil.getSystemUserName()))
|
||||||
|
{
|
||||||
|
createPerson(AuthenticationUtil.getSystemUserName(), false);
|
||||||
|
}
|
||||||
|
assertTrue("No person object for System available.", authDao.authorityExists(AuthenticationUtil.getSystemUserName()));
|
||||||
|
|
||||||
|
siteId = GUID.generate();
|
||||||
|
siteService.createSite(
|
||||||
|
"rm-site-dashboard",
|
||||||
|
siteId,
|
||||||
|
"title",
|
||||||
|
"descrition",
|
||||||
|
SiteVisibility.PUBLIC,
|
||||||
|
RecordsManagementModel.TYPE_RM_SITE);
|
||||||
|
|
||||||
|
filePlan = siteService.getContainer(siteId, RmSiteType.COMPONENT_DOCUMENT_LIBRARY);
|
||||||
|
assertNotNull("Site document library container was not created successfully.", filePlan);
|
||||||
|
|
||||||
|
// Create RM container
|
||||||
|
rmContainer = filePlanService.createRecordCategory(filePlan, "rmContainer");
|
||||||
|
assertNotNull("Could not create rm container", rmContainer);
|
||||||
|
|
||||||
|
// Create RM folder
|
||||||
|
rmFolder = recordFolderService.createRecordFolder(rmContainer, "rmFolder");
|
||||||
|
assertNotNull("Could not create rm folder", rmFolder);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mid-level user further downgrading a downgraded record.
|
||||||
|
* <p>
|
||||||
|
* <a href="https://issues.alfresco.com/jira/browse/RM-2502">RM-2502</a><pre>
|
||||||
|
* Given I have "level2" clearance
|
||||||
|
* And a record has an initial classification of "level1"
|
||||||
|
* And the record has a current classification of "level2"
|
||||||
|
* When I try to downgrade the record to "level3"
|
||||||
|
* Then I am successful.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testInitialClassificationConstraint()
|
||||||
|
{
|
||||||
|
// Given I set up some test data (admin at level 1, midLevelUser at level 2 and a new record).
|
||||||
|
final String midLevelUser = GUID.generate();
|
||||||
|
final NodeRef record = AuthenticationUtil.runAsSystem(new RunAsWork<NodeRef>()
|
||||||
|
{
|
||||||
|
public NodeRef doWork()
|
||||||
|
{
|
||||||
|
// Ensure admin is level 1 cleared.
|
||||||
|
securityClearanceService.setUserSecurityClearance(AuthenticationUtil.getAdminUserName(), "level1");
|
||||||
|
// Create user with level 2 clearance.
|
||||||
|
createPerson(midLevelUser, true);
|
||||||
|
filePlanRoleService.assignRoleToAuthority(filePlan, FilePlanRoleService.ROLE_RECORDS_MANAGER, midLevelUser);
|
||||||
|
filePlanPermissionService.setPermission(rmContainer, midLevelUser, FILING);
|
||||||
|
securityClearanceService.setUserSecurityClearance(midLevelUser, "level2");
|
||||||
|
// Create a record to be classified during the test.
|
||||||
|
return utils.createRecord(rmFolder, RECORD_NAME);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// And admin creates a downgraded record.
|
||||||
|
AuthenticationUtil.runAs(new RunAsWork<Void>()
|
||||||
|
{
|
||||||
|
public Void doWork()
|
||||||
|
{
|
||||||
|
// Create a level 1 record and downgrade it to level 2.
|
||||||
|
classifyAs(record, "level1");
|
||||||
|
classifyAs(record, "level2");
|
||||||
|
|
||||||
|
assertTrue("Record should have been classified.",
|
||||||
|
nodeService.hasAspect(record, ClassifiedContentModel.ASPECT_CLASSIFIED));
|
||||||
|
assertEquals("Record have initial classification of 'level1'.", "level1",
|
||||||
|
nodeService.getProperty(record, ClassifiedContentModel.PROP_INITIAL_CLASSIFICATION));
|
||||||
|
assertEquals("Record should be 'level2' classified.", "level2",
|
||||||
|
nodeService.getProperty(record, ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, AuthenticationUtil.getAdminUserName());
|
||||||
|
|
||||||
|
// When the mid-level user downgrades the record to level 3.
|
||||||
|
AuthenticationUtil.runAs(new RunAsWork<Void>()
|
||||||
|
{
|
||||||
|
public Void doWork()
|
||||||
|
{
|
||||||
|
// Check that the mid-clearance user can further downgrade the classification (even though the initial
|
||||||
|
// classification was above their clearance).
|
||||||
|
classifyAs(record, "level3");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, midLevelUser);
|
||||||
|
|
||||||
|
// Then the record is classified at level 3 (with initial classification level 1).
|
||||||
|
AuthenticationUtil.runAs(new RunAsWork<Void>()
|
||||||
|
{
|
||||||
|
public Void doWork()
|
||||||
|
{
|
||||||
|
assertTrue("Record should still be classified.",
|
||||||
|
nodeService.hasAspect(record, ClassifiedContentModel.ASPECT_CLASSIFIED));
|
||||||
|
assertEquals("Record have initial classification of 'level1'.", "level1",
|
||||||
|
nodeService.getProperty(record, ClassifiedContentModel.PROP_INITIAL_CLASSIFICATION));
|
||||||
|
assertEquals("Record should be 'level3' classified.", "level3",
|
||||||
|
nodeService.getProperty(record, ClassifiedContentModel.PROP_CURRENT_CLASSIFICATION));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, AuthenticationUtil.getAdminUserName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util method to create a person.
|
||||||
|
*
|
||||||
|
* @param userName user name
|
||||||
|
* @param createAuth Whether to give the user a password or not.
|
||||||
|
* @return NodeRef user node reference
|
||||||
|
*/
|
||||||
|
private NodeRef createPerson(String userName, boolean createAuth)
|
||||||
|
{
|
||||||
|
if (createAuth)
|
||||||
|
{
|
||||||
|
authenticationService.createAuthentication(userName, "password".toCharArray());
|
||||||
|
}
|
||||||
|
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
||||||
|
properties.put(ContentModel.PROP_USERNAME, userName);
|
||||||
|
return personService.createPerson(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classify the given node.
|
||||||
|
*
|
||||||
|
* @param node The node to classify.
|
||||||
|
* @param level The id of the classification level to use.
|
||||||
|
*/
|
||||||
|
private void classifyAs(final NodeRef node, final String level)
|
||||||
|
{
|
||||||
|
ClassificationAspectProperties propertiesDTO = new ClassificationAspectProperties();
|
||||||
|
propertiesDTO.setClassificationLevelId(level);
|
||||||
|
propertiesDTO.setClassifiedBy(CLASSIFIED_BY);
|
||||||
|
propertiesDTO.setClassificationReasonIds(Collections.singleton(CLASSIFICATION_REASON));
|
||||||
|
contentClassificationService.editClassifiedContent(propertiesDTO, node);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user