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">
|
||||
<value>
|
||||
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.getClassificationLevelById=ACL_ALLOW
|
||||
org.alfresco.module.org_alfresco_module_rm.classification.ClassificationSchemeService.getClassificationReasonById=ACL_ALLOW
|
||||
|
@@ -26,6 +26,8 @@
|
||||
|
||||
<!-- Model Constraints -->
|
||||
<constraints>
|
||||
<constraint name="clf:initialClassificationLevelRestriction"
|
||||
type="org.alfresco.module.org_alfresco_module_rm.classification.InitialClassificationLevelConstraint" />
|
||||
<constraint name="clf:classificationLevelRestriction"
|
||||
type="org.alfresco.module.org_alfresco_module_rm.classification.ClassificationLevelConstraint" />
|
||||
<constraint name="clf:classificationReasonRestriction"
|
||||
@@ -53,7 +55,7 @@
|
||||
<protected>true</protected>
|
||||
<mandatory>true</mandatory>
|
||||
<constraints>
|
||||
<constraint ref="clf:classificationLevelRestriction" />
|
||||
<constraint ref="clf:initialClassificationLevelRestriction" />
|
||||
</constraints>
|
||||
</property>
|
||||
<property name="clf:currentClassification">
|
||||
|
@@ -44,6 +44,15 @@ public interface ClassificationSchemeService
|
||||
*/
|
||||
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.
|
||||
* @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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClassificationLevel> getAllClassificationLevels()
|
||||
{
|
||||
if (levelManager == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return levelManager.getClassificationLevels();
|
||||
}
|
||||
|
||||
@Override public List<ClassificationReason> getClassificationReasons()
|
||||
{
|
||||
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