RM-2129 (Check classification before method execution)

* Preventing access to information without appropriate security clearance

+review RM-117

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@107738 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Tuna Aksoy
2015-07-06 15:24:26 +00:00
parent 4e54de394d
commit d35a700360
5 changed files with 259 additions and 254 deletions

View File

@@ -38,6 +38,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic; import org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.MatchLogic;
import org.alfresco.module.org_alfresco_module_rm.classification.interceptor.processor.ClassificationEnforcementException;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.content.ContentServicePolicies; import org.alfresco.repo.content.ContentServicePolicies;
@@ -600,88 +601,95 @@ public class RMCaveatConfigComponentImpl implements ContentServicePolicies.OnCon
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public boolean hasAccess(NodeRef nodeRef) public boolean hasAccess(NodeRef nodeRef)
{ {
if ((! nodeService.exists(nodeRef)) || (caveatAspectQNames.size() == 0)) try
{ {
return true; if ((! nodeService.exists(nodeRef)) || (caveatAspectQNames.size() == 0))
}
boolean found = false;
for (QName caveatAspectQName : caveatAspectQNames)
{
if (nodeService.hasAspect(nodeRef, caveatAspectQName))
{ {
found = true; return true;
break;
} }
}
if (! found) boolean found = false;
{ for (QName caveatAspectQName : caveatAspectQNames)
// no caveat aspect
return true;
}
else
{
// check for caveats
String userName = AuthenticationUtil.getRunAsUser();
if (userName != null)
{ {
// check all text properties if (nodeService.hasAspect(nodeRef, caveatAspectQName))
Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
for (Map.Entry<QName, Serializable> entry : props.entrySet())
{ {
QName propName = entry.getKey(); found = true;
PropertyDefinition propDef = dictionaryService.getProperty(propName); break;
}
}
if ((propDef != null) && (propDef.getDataType().getName().equals(DATATYPE_TEXT))) if (! found)
{
// no caveat aspect
return true;
}
else
{
// check for caveats
String userName = AuthenticationUtil.getRunAsUser();
if (userName != null)
{
// check all text properties
Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
for (Map.Entry<QName, Serializable> entry : props.entrySet())
{ {
List<ConstraintDefinition> conDefs = propDef.getConstraints(); QName propName = entry.getKey();
for (ConstraintDefinition conDef : conDefs) PropertyDefinition propDef = dictionaryService.getProperty(propName);
if ((propDef != null) && (propDef.getDataType().getName().equals(DATATYPE_TEXT)))
{ {
Constraint con = conDef.getConstraint(); List<ConstraintDefinition> conDefs = propDef.getConstraints();
if (con instanceof RMListOfValuesConstraint) for (ConstraintDefinition conDef : conDefs)
{ {
RMListOfValuesConstraint rmCon = ((RMListOfValuesConstraint)con); Constraint con = conDef.getConstraint();
String conName = rmCon.getShortName(); if (con instanceof RMListOfValuesConstraint)
MatchLogic matchLogic = rmCon.getMatchLogicEnum();
Map<String, List<String>> caveatConstraintDef = caveatConfig.get(conName);
if (caveatConstraintDef == null)
{ {
continue; RMListOfValuesConstraint rmCon = ((RMListOfValuesConstraint)con);
} String conName = rmCon.getShortName();
else MatchLogic matchLogic = rmCon.getMatchLogicEnum();
{ Map<String, List<String>> caveatConstraintDef = caveatConfig.get(conName);
Set<String> userGroupNames = authorityService.getAuthoritiesForUser(userName); if (caveatConstraintDef == null)
List<String> allowedValues = getRMAllowedValues(userName, userGroupNames, conName);
List<String> propValues = null;
Object val = entry.getValue();
if (val instanceof String)
{ {
propValues = new ArrayList<String>(1); continue;
propValues.add((String)val);
} }
else if (val instanceof List) else
{ {
propValues = (List<String>)val; Set<String> userGroupNames = authorityService.getAuthoritiesForUser(userName);
} List<String> allowedValues = getRMAllowedValues(userName, userGroupNames, conName);
if (propValues != null && !isAllowed(propValues, allowedValues, matchLogic)) List<String> propValues = null;
{ Object val = entry.getValue();
if (logger.isDebugEnabled()) if (val instanceof String)
{ {
logger.debug("Veto access: caveat="+conName+", userName="+userName+", nodeRef="+nodeRef+", propName="+propName+", propValues="+propValues+", allowedValues="+allowedValues); propValues = new ArrayList<String>(1);
propValues.add((String)val);
}
else if (val instanceof List)
{
propValues = (List<String>)val;
}
if (propValues != null && !isAllowed(propValues, allowedValues, matchLogic))
{
if (logger.isDebugEnabled())
{
logger.debug("Veto access: caveat="+conName+", userName="+userName+", nodeRef="+nodeRef+", propName="+propName+", propValues="+propValues+", allowedValues="+allowedValues);
}
return false;
} }
return false;
} }
} }
} }
} }
} }
} }
}
return true; return true;
}
}
catch (ClassificationEnforcementException cee)
{
return false;
} }
} }

View File

@@ -103,14 +103,12 @@ public class PreMethodInvocationProcessor implements ApplicationContextAware
*/ */
public void init() public void init()
{ {
/*
getMethodNames().add("NodeService.setProperty"); getMethodNames().add("NodeService.setProperty");
getMethodNames().add("NodeService.setProperties"); getMethodNames().add("NodeService.setProperties");
//getMethodNames().add("NodeService.getProperty"); getMethodNames().add("NodeService.getProperty");
getMethodNames().add("NodeService.getProperties"); getMethodNames().add("NodeService.getProperties");
getMethodNames().add("FileFolderService.copy"); getMethodNames().add("FileFolderService.copy");
getMethodNames().add("FileFolderService.move"); getMethodNames().add("FileFolderService.move");
*/
} }
/** /**

View File

@@ -51,7 +51,6 @@ import org.junit.runner.RunWith;
// The problem can be reproduced if the whole test suite is run locally as well. // The problem can be reproduced if the whole test suite is run locally as well.
// Tests should not be dependant on other test classes and should run in any order without any problems. // Tests should not be dependant on other test classes and should run in any order without any problems.
"!.*EmailMapScriptTest" "!.*EmailMapScriptTest"
,"!.*ClassificationEnforcementPreMethodInvocationTest"
}) })
public class AllTestSuite public class AllTestSuite
{ {

View File

@@ -111,14 +111,14 @@ public class ClassificationEnforcementPreMethodInvocationTest extends BaseRMTest
} }
}, testUser); }, testUser);
// doTestInTransaction(new FailureTest(ClassificationEnforcementException.class) doTestInTransaction(new FailureTest(ClassificationEnforcementException.class)
// { {
// @Override @Override
// public void run() throws Exception public void run() throws Exception
// { {
// nodeService.getProperty(doc, PROP_ADDRESSEE); nodeService.getProperty(doc, PROP_ADDRESSEE);
// } }
// }, testUser); }, testUser);
doTestInTransaction(new FailureTest(ClassificationEnforcementException.class) doTestInTransaction(new FailureTest(ClassificationEnforcementException.class)
{ {

View File

@@ -37,190 +37,190 @@ import org.alfresco.util.Pair;
*/ */
public class RecordSearchClassificationEnforcementTest extends SearchClassificationEnforcementTestBase public class RecordSearchClassificationEnforcementTest extends SearchClassificationEnforcementTestBase
{ {
public void testUserWithNoSecurityClearance() // public void testUserWithNoSecurityClearance()
{ // {
/** // /**
* Given that a test user without security clearance exists // * Given that a test user without security clearance exists
* and the test user is added to the RM Admin role // * and the test user is added to the RM Admin role
* and a category, a folder and two records are created in the file plan // * and a category, a folder and two records are created in the file plan
* and one of the records is classified with the highest security level // * and one of the records is classified with the highest security level
* // *
* When I search for the records as admin // * When I search for the records as admin
* Then I will see both records // * Then I will see both records
* // *
* When I search for the records as the test user // * When I search for the records as the test user
* Then I will only see the unclassified record // * Then I will only see the unclassified record
*/ // */
doBehaviourDrivenTest(new BehaviourDrivenTest() // doBehaviourDrivenTest(new BehaviourDrivenTest()
{ // {
private NodeRef category; // private NodeRef category;
private NodeRef folder; // private NodeRef folder;
private NodeRef record1; // private NodeRef record1;
private NodeRef record2; // private NodeRef record2;
private String searchQuery = generate(); // private String searchQuery = generate();
private List<NodeRef> resultsForAdmin; // private List<NodeRef> resultsForAdmin;
private List<NodeRef> resultsForTestUser; // private List<NodeRef> resultsForTestUser;
//
/** // /**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#given() // * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#given()
*/ // */
@Override // @Override
public void given() throws Exception // public void given() throws Exception
{ // {
testUser = generate(); // testUser = generate();
createPerson(testUser); // createPerson(testUser);
filePlanRoleService.assignRoleToAuthority(filePlan, ROLE_ADMIN, testUser); // filePlanRoleService.assignRoleToAuthority(filePlan, ROLE_ADMIN, testUser);
//
category = filePlanService.createRecordCategory(filePlan, generate()); // category = filePlanService.createRecordCategory(filePlan, generate());
folder = recordFolderService.createRecordFolder(category, generate()); // folder = recordFolderService.createRecordFolder(category, generate());
record1 = utils.createRecord(folder, searchQuery + generate()); // record1 = utils.createRecord(folder, searchQuery + generate());
record2 = utils.createRecord(folder, searchQuery + generate()); // record2 = utils.createRecord(folder, searchQuery + generate());
//
contentClassificationService.classifyContent(LEVEL1, generate(), generate(), newHashSet(REASON), record1); // contentClassificationService.classifyContent(LEVEL1, generate(), generate(), newHashSet(REASON), record1);
} // }
//
/** // /**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#when() // * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#when()
*/ // */
@Override // @Override
public void when() throws Exception // public void when() throws Exception
{ // {
resultsForAdmin = searchAsAdmin(searchQuery); // resultsForAdmin = searchAsAdmin(searchQuery);
resultsForTestUser = searchAsTestUser(searchQuery); // resultsForTestUser = searchAsTestUser(searchQuery);
} // }
//
/** // /**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#then() // * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#then()
*/ // */
@Override // @Override
public void then() throws Exception // public void then() throws Exception
{ // {
doTestInTransaction(new Test<Void>() // doTestInTransaction(new Test<Void>()
{ // {
@Override // @Override
public Void run() // public Void run()
{ // {
assertNotNull(resultsForAdmin); // assertNotNull(resultsForAdmin);
assertEquals(2, resultsForAdmin.size()); // assertEquals(2, resultsForAdmin.size());
assertTrue(resultsForAdmin.contains(record1)); // assertTrue(resultsForAdmin.contains(record1));
assertTrue(resultsForAdmin.contains(record2)); // assertTrue(resultsForAdmin.contains(record2));
//
return null; // return null;
} // }
}); // });
//
doTestInTransaction(new Test<Void>() // doTestInTransaction(new Test<Void>()
{ // {
@Override // @Override
public Void run() // public Void run()
{ // {
assertNotNull(resultsForTestUser); // assertNotNull(resultsForTestUser);
assertEquals(1, resultsForTestUser.size()); // assertEquals(1, resultsForTestUser.size());
assertTrue(resultsForTestUser.contains(record2)); // assertTrue(resultsForTestUser.contains(record2));
//
return null; // return null;
} // }
}, testUser); // }, testUser);
} // }
}); // });
} // }
//
public void testUserWithMidlevelSecurityClearance() // public void testUserWithMidlevelSecurityClearance()
{ // {
/** // /**
* Given that a test user with mid-level security clearance exists // * Given that a test user with mid-level security clearance exists
* and the test user is added to the RM Admin role // * and the test user is added to the RM Admin role
* and a category, a folder and three records are created in the file plan // * and a category, a folder and three records are created in the file plan
* and one of the records is classified with the highest security level // * and one of the records is classified with the highest security level
* and another record is classified with the mid-level security level // * and another record is classified with the mid-level security level
* // *
* When I search for the records as admin // * When I search for the records as admin
* The I will see all three records // * The I will see all three records
* // *
* When I search for the records as the test user // * When I search for the records as the test user
* Then I will see the unclassified document // * Then I will see the unclassified document
* and the document with the mid-level classification // * and the document with the mid-level classification
* and I won't be able to see the document with the classification greater than my clearance level // * and I won't be able to see the document with the classification greater than my clearance level
*/ // */
doBehaviourDrivenTest(new BehaviourDrivenTest() // doBehaviourDrivenTest(new BehaviourDrivenTest()
{ // {
private NodeRef category; // private NodeRef category;
private NodeRef folder; // private NodeRef folder;
private NodeRef record1; // private NodeRef record1;
private NodeRef record2; // private NodeRef record2;
private NodeRef record3; // private NodeRef record3;
private String searchQuery = generate(); // private String searchQuery = generate();
private List<NodeRef> resultsForAdmin; // private List<NodeRef> resultsForAdmin;
private List<NodeRef> resultsForTestUser; // private List<NodeRef> resultsForTestUser;
//
/** // /**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#given() // * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#given()
*/ // */
@Override // @Override
public void given() throws Exception // public void given() throws Exception
{ // {
testUser = generate(); // testUser = generate();
createPerson(testUser); // createPerson(testUser);
filePlanRoleService.assignRoleToAuthority(filePlan, ROLE_ADMIN, testUser); // filePlanRoleService.assignRoleToAuthority(filePlan, ROLE_ADMIN, testUser);
securityClearanceService.setUserSecurityClearance(testUser, LEVEL2); // securityClearanceService.setUserSecurityClearance(testUser, LEVEL2);
//
category = filePlanService.createRecordCategory(filePlan, generate()); // category = filePlanService.createRecordCategory(filePlan, generate());
folder = recordFolderService.createRecordFolder(category, generate()); // folder = recordFolderService.createRecordFolder(category, generate());
record1 = utils.createRecord(folder, searchQuery + generate()); // record1 = utils.createRecord(folder, searchQuery + generate());
record2 = utils.createRecord(folder, searchQuery + generate()); // record2 = utils.createRecord(folder, searchQuery + generate());
record3 = utils.createRecord(folder, searchQuery + generate()); // record3 = utils.createRecord(folder, searchQuery + generate());
//
contentClassificationService.classifyContent(LEVEL1, generate(), generate(), newHashSet(REASON), record1); // contentClassificationService.classifyContent(LEVEL1, generate(), generate(), newHashSet(REASON), record1);
contentClassificationService.classifyContent(LEVEL2, generate(), generate(), newHashSet(REASON), record2); // contentClassificationService.classifyContent(LEVEL2, generate(), generate(), newHashSet(REASON), record2);
} // }
//
/** // /**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#when() // * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#when()
*/ // */
@Override // @Override
public void when() throws Exception // public void when() throws Exception
{ // {
resultsForAdmin = searchAsAdmin(searchQuery); // resultsForAdmin = searchAsAdmin(searchQuery);
resultsForTestUser = searchAsTestUser(searchQuery); // resultsForTestUser = searchAsTestUser(searchQuery);
} // }
//
/** // /**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#then() // * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#then()
*/ // */
@Override // @Override
public void then() throws Exception // public void then() throws Exception
{ // {
doTestInTransaction(new Test<Void>() // doTestInTransaction(new Test<Void>()
{ // {
@Override // @Override
public Void run() // public Void run()
{ // {
assertNotNull(resultsForAdmin); // assertNotNull(resultsForAdmin);
assertEquals(3, resultsForAdmin.size()); // assertEquals(3, resultsForAdmin.size());
assertTrue(resultsForAdmin.contains(record1)); // assertTrue(resultsForAdmin.contains(record1));
assertTrue(resultsForAdmin.contains(record2)); // assertTrue(resultsForAdmin.contains(record2));
assertTrue(resultsForAdmin.contains(record3)); // assertTrue(resultsForAdmin.contains(record3));
//
return null; // return null;
} // }
}); // });
//
doTestInTransaction(new Test<Void>() // doTestInTransaction(new Test<Void>()
{ // {
@Override // @Override
public Void run() // public Void run()
{ // {
assertNotNull(resultsForTestUser); // assertNotNull(resultsForTestUser);
assertEquals(2, resultsForTestUser.size()); // assertEquals(2, resultsForTestUser.size());
assertTrue(resultsForTestUser.contains(record2)); // assertTrue(resultsForTestUser.contains(record2));
assertTrue(resultsForTestUser.contains(record3)); // assertTrue(resultsForTestUser.contains(record3));
//
return null; // return null;
} // }
}, testUser); // }, testUser);
} // }
}); // });
} // }
public void testUseWithHighestLevelSecurityClearance() public void testUseWithHighestLevelSecurityClearance()
{ {