From e6fe911fcfe31a886726584fef6a2557978ce204 Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Thu, 2 Jul 2015 18:05:18 +0000 Subject: [PATCH] RM-2129 (Check classification before method execution) +review RM @rwetherall git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@107649 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../classified-content-context.xml | 8 - .../ClassificationServiceBootstrap.java | 8 - .../ClassificationMethodInterceptor.java | 23 +- .../PostMethodInvocationProcessor.java | 41 ++- .../PreMethodInvocationProcessor.java | 249 ++++-------------- ...ionEnforcementPreMethodInvocationTest.java | 128 +++++++++ 6 files changed, 207 insertions(+), 250 deletions(-) create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/interceptor/ClassificationEnforcementPreMethodInvocationTest.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml index 570ec94695..939558bd7a 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/classified-content-context.xml @@ -44,14 +44,6 @@ - exemptionCategoryValidator = new ClassificationSchemeEntityValidator<>(exemptionCategoryFieldsValidator); - private boolean isInitialised = false; - public ClassificationServiceBootstrap(AuthenticationUtil authUtil, TransactionService txService, AttributeService attributeService, @@ -95,11 +93,6 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem public ExemptionCategoryManager getExemptionCategoryManager() { return exemptionCategoryManager; } public ClearanceLevelManager getClearanceLevelManager() { return clearanceLevelManager; } - public boolean isInitialised() - { - return isInitialised; - } - @Override public void onBootstrap(ApplicationEvent event) { authenticationUtil.runAsSystem(new org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork() @@ -123,7 +116,6 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem exemptionCategoryManager.setExemptionCategories(exemptionCategories); initConfiguredClearanceLevels(classificationLevelManager.getClassificationLevels()); - isInitialised = true; return null; } }; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptor.java index f3a366b6c6..eeb007a033 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptor.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptor.java @@ -83,28 +83,21 @@ public class ClassificationMethodInterceptor implements MethodInterceptor { mandatory("invocation", invocation); - Object result = null; - - boolean canProceed = true; boolean isUserValid = isUserValid(); + // Pre method invocation processing if (isUserValid) { - //FIXME!!! - // Pre method invocation processing - //canProceed = getPreMethodInvocationProcessor().process(invocation); + getPreMethodInvocationProcessor().process(invocation); } - if (canProceed) - { - // Method invocation - result = invocation.proceed(); + // Method invocation + Object result = invocation.proceed(); - // Post method invocation processing - if (isUserValid && result != null) - { - result = getPostMethodInvocationProcessor().process(result); - } + // Post method invocation processing + if (isUserValid && result != null) + { + result = getPostMethodInvocationProcessor().process(result); } return result; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PostMethodInvocationProcessor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PostMethodInvocationProcessor.java index 775d03a169..90082c7272 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PostMethodInvocationProcessor.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PostMethodInvocationProcessor.java @@ -26,8 +26,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import org.apache.log4j.Logger; - /** * Registry for post method invocation processors * @@ -36,11 +34,8 @@ import org.apache.log4j.Logger; */ public class PostMethodInvocationProcessor { - /** Logger */ - private static Logger LOG = Logger.getLogger(PostMethodInvocationProcessor.class); - /** Post method invocation processors */ - private Map, BasePostMethodInvocationProcessor> processors = new HashMap, BasePostMethodInvocationProcessor>(); + private Map, BasePostMethodInvocationProcessor> processors = new HashMap<>(); /** * Registers a post method invocation processor @@ -51,7 +46,7 @@ public class PostMethodInvocationProcessor { mandatory("object", object); - processors.put(object.getClassName(), object); + getProcessors().put(object.getClassName(), object); } /** @@ -72,25 +67,27 @@ public class PostMethodInvocationProcessor */ protected BasePostMethodInvocationProcessor getProcessor(Object object) { - mandatory("object", object); - BasePostMethodInvocationProcessor result = null; - Class clazz = object.getClass(); - if (clazz.isArray()) + if (object != null) { - result = getProcessors().get(Array.class); - } + Class clazz = object.getClass(); - if (result == null) - { - Set, BasePostMethodInvocationProcessor>> processorsEntrySet = getProcessors().entrySet(); - for (Map.Entry, BasePostMethodInvocationProcessor> processorEntry : processorsEntrySet) + if (clazz.isArray()) { - if (processorEntry.getKey().isAssignableFrom(clazz)) + result = getProcessors().get(Array.class); + } + + if (result == null) + { + Set, BasePostMethodInvocationProcessor>> processorsEntrySet = getProcessors().entrySet(); + for (Map.Entry, BasePostMethodInvocationProcessor> processorEntry : processorsEntrySet) { - result = processorEntry.getValue(); - break; + if (processorEntry.getKey().isAssignableFrom(clazz)) + { + result = processorEntry.getValue(); + break; + } } } } @@ -115,10 +112,6 @@ public class PostMethodInvocationProcessor { result = processor.process(result); } - else - { - LOG.debug("No processor found for '" + result + "'."); - } } return result; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PreMethodInvocationProcessor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PreMethodInvocationProcessor.java index 85585653be..b74e636a55 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PreMethodInvocationProcessor.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/processor/PreMethodInvocationProcessor.java @@ -18,22 +18,18 @@ */ package org.alfresco.module.org_alfresco_module_rm.classification.interceptor.processor; -import static java.lang.Boolean.TRUE; +import static com.google.common.collect.Lists.newArrayList; import static org.alfresco.model.ContentModel.TYPE_CONTENT; -import static org.alfresco.util.GUID.generate; import static org.alfresco.util.ParameterCheck.mandatory; import java.lang.reflect.Method; +import java.util.List; -import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationServiceBootstrap; import org.alfresco.module.org_alfresco_module_rm.classification.ContentClassificationService; -import org.alfresco.module.org_alfresco_module_rm.util.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.security.permissions.AccessDeniedException; 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.transaction.TransactionService; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -48,221 +44,88 @@ import org.springframework.context.ApplicationContextAware; */ public class PreMethodInvocationProcessor implements ApplicationContextAware { - /** Key to mark the transaction as processing */ - private static final String KEY_PROCESSING = generate(); - + /** Application context */ private ApplicationContext applicationContext; + /** + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } + /** + * Gets the content classification service + * + * @return The content classification service + */ protected ContentClassificationService getContentClassificationService() { - return (ContentClassificationService)applicationContext.getBean("contentClassificationService"); - } - - protected AlfrescoTransactionSupport getAlfrescoTransactionSupport() - { - return (AlfrescoTransactionSupport)applicationContext.getBean("rm.alfrescoTransactionSupport"); - } - - protected RetryingTransactionHelper getRetryingTransactionHelper() - { - return ((TransactionService)applicationContext.getBean("transactionService")).getRetryingTransactionHelper(); - } - - protected ClassificationServiceBootstrap getClassificationServiceBootstrap() - { - return (ClassificationServiceBootstrap)applicationContext.getBean("classificationServiceBootstrap"); + return (ContentClassificationService) applicationContext.getBean("contentClassificationService"); } + /** + * Gets the node service + * + * @return The node service + */ protected NodeService getNodeService() { - return (NodeService)applicationContext.getBean("dbNodeService"); + return (NodeService) applicationContext.getBean("dbNodeService"); } + /** + * Gets the dictionary service + * + * @return The dictionary service + */ protected DictionaryService getDictionaryService() { - return (DictionaryService)applicationContext.getBean("dictionaryService"); + return (DictionaryService) applicationContext.getBean("dictionaryService"); } -// /** Transaction service */ -// private TransactionService transactionService; -// -// /** Classification service bootstrap */ -// private ClassificationServiceBootstrap classificationServiceBootstrap; -// -// /** Alfresco transaction support */ -// private AlfrescoTransactionSupport alfrescoTransactionSupport; -// -// /** Node service */ -// private NodeService nodeService; -// -// /** Dictionary service */ -// private DictionaryService dictionaryService; -// -// /** Content classification service */ -// private ContentClassificationService contentClassificationService; -// -// /** -// * @return the transactionService -// */ -// protected TransactionService getTransactionService() -// { -// return this.transactionService; -// } -// -// /** -// * @return the classificationServiceBootstrap -// */ -// protected ClassificationServiceBootstrap getClassificationServiceBootstrap() -// { -// return this.classificationServiceBootstrap; -// } -// -// /** -// * @return the alfrescoTransactionSupport -// */ -// protected AlfrescoTransactionSupport getAlfrescoTransactionSupport() -// { -// return this.alfrescoTransactionSupport; -// } -// -// /** -// * @return the nodeService -// */ -// protected NodeService getNodeService() -// { -// return this.nodeService; -// } -// -// /** -// * @return the dictionaryService -// */ -// protected DictionaryService getDictionaryService() -// { -// return this.dictionaryService; -// } -// -// /** -// * @return the contentClassificationService -// */ -// protected ContentClassificationService getContentClassificationService() -// { -// return this.contentClassificationService; -// } -// -// /** -// * @param transactionService the transactionService to set -// */ -// public void setTransactionService(TransactionService transactionService) -// { -// this.transactionService = transactionService; -// } -// -// /** -// * @param classificationServiceBootstrap the classificationServiceBootstrap to set -// */ -// public void setClassificationServiceBootstrap(ClassificationServiceBootstrap classificationServiceBootstrap) -// { -// this.classificationServiceBootstrap = classificationServiceBootstrap; -// } -// -// /** -// * @param alfrescoTransactionSupport the alfrescoTransactionSupport to set -// */ -// public void setAlfrescoTransactionSupport(AlfrescoTransactionSupport alfrescoTransactionSupport) -// { -// this.alfrescoTransactionSupport = alfrescoTransactionSupport; -// } -// -// /** -// * @param nodeService the nodeService to set -// */ -// public void setNodeService(NodeService nodeService) -// { -// this.nodeService = nodeService; -// } -// -// /** -// * @param dictionaryService the dictionaryService to set -// */ -// public void setDictionaryService(DictionaryService dictionaryService) -// { -// this.dictionaryService = dictionaryService; -// } -// -// /** -// * @param contentClassificationService the contentClassificationService to set -// */ -// public void setContentClassificationService(ContentClassificationService contentClassificationService) -// { -// this.contentClassificationService = contentClassificationService; -// } - /** * Checks if the current user is cleared to see the items * passed as parameters to the current method invocation. * * @param invocation The current method invocation - * @return true if the user is cleared to see the items, false otherwise */ - public boolean process(final MethodInvocation invocation) + public void process(MethodInvocation invocation) { mandatory("invocation", invocation); - // do in transaction - return /*getTransactionService().*/getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + Method method = invocation.getMethod(); + String className = method.getDeclaringClass().getSimpleName(); + String methodName = method.getName(); + String name = className + "." + methodName; + Object[] args = invocation.getArguments(); + + if (getMethodNames().contains(name)) { - @SuppressWarnings("rawtypes") - public Boolean execute() throws Throwable + for (Object arg : args) { - Boolean result = true; - - // ensure classification service has been bootstrapped - if (getClassificationServiceBootstrap().isInitialised()) + if (arg != null && NodeRef.class.isAssignableFrom(arg.getClass())) { - // check that we are not already processing a classification check - Object value = getAlfrescoTransactionSupport().getResource(KEY_PROCESSING); - if (value == null) - { - Method method = invocation.getMethod(); - Class[] params = method.getParameterTypes(); - - int position = 0; - for (Class param : params) - { - // if the param is a node reference - if (NodeRef.class.isAssignableFrom(param)) - { - // mark the transaction as processing a classification check - getAlfrescoTransactionSupport().bindResource(KEY_PROCESSING, TRUE); - try - { - // get the value of the parameter - NodeRef testNodeRef = (NodeRef) invocation.getArguments()[position]; - - // if node exists then see if the current user has clearance - result = isNodeCleared(testNodeRef); - } - finally - { - // clear the transaction as processed a classification check - getAlfrescoTransactionSupport().unbindResource(KEY_PROCESSING); - } - } - - position++; - } - } + isNodeCleared(((NodeRef) arg), name); } - - return result; } - }, true); + } + } + + /** + * Returns a list of method names to check before invocation + * + * @return List of method names to check before invocation + */ + private List getMethodNames() + { + return newArrayList( + "NodeService.setProperty", + //"NodeService.getProperty", + "FileFolderService.copy" + ); } /** @@ -270,19 +133,15 @@ public class PreMethodInvocationProcessor implements ApplicationContextAware * the currently logged in user is cleared to see it. * * @param nodeRef Node reference to check - * @return true if the node passes the checks, false otherwise + * @param name The name of the invoked method */ - private boolean isNodeCleared(NodeRef nodeRef) + private void isNodeCleared(NodeRef nodeRef, String name) { - boolean result = true; - if (getNodeService().exists(nodeRef) && getDictionaryService().isSubClass(getNodeService().getType(nodeRef), TYPE_CONTENT) && !getContentClassificationService().hasClearance(nodeRef)) { - result = false; + throw new AccessDeniedException("The method '" + name + "' was called, but you are not cleared for the node."); } - - return result; } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/interceptor/ClassificationEnforcementPreMethodInvocationTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/interceptor/ClassificationEnforcementPreMethodInvocationTest.java new file mode 100644 index 0000000000..e3fb80a663 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/interceptor/ClassificationEnforcementPreMethodInvocationTest.java @@ -0,0 +1,128 @@ +/* + * 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 . + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.classification.interceptor; + +import static com.google.common.collect.Sets.newHashSet; +import static org.alfresco.repo.site.SiteModel.SITE_MANAGER; +import static org.alfresco.util.GUID.generate; + +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Classification enforcement pre method invocation test + * + * @author Tuna Aksoy + * @since 3.0 + */ +public class ClassificationEnforcementPreMethodInvocationTest extends BaseRMTestCase +{ + private String testUser; + private static final String LEVEL1 = "level1"; + private static final String REASON = "Test Reason 1"; + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#isCollaborationSiteTest() + */ + @Override + protected boolean isCollaborationSiteTest() + { + return true; + } + + public void testClassificationEnforcementPreMethodInvocation() + { + /** + * Given that I am a site manager and not cleared to see a document + * When I try to set a property on that document an exception will be thrown + * and I try to copy that document an exception will be thrown + */ + doBehaviourDrivenTest(new BehaviourDrivenTest() + { + private NodeRef folder1; + private NodeRef folder2; + private NodeRef doc1; + private NodeRef doc2; + private String propertyValue = generate(); + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#given() + */ + @Override + public void given() throws Exception + { + testUser = generate(); + createPerson(testUser); + siteService.setMembership(collabSiteId, testUser, SITE_MANAGER); + + folder1 = fileFolderService.create(documentLibrary, generate(), TYPE_FOLDER).getNodeRef(); + folder2 = fileFolderService.create(documentLibrary, generate(), TYPE_FOLDER).getNodeRef(); + doc1 = fileFolderService.create(folder1, generate(), TYPE_CONTENT).getNodeRef(); + doc2 = fileFolderService.create(folder1, generate(), TYPE_CONTENT).getNodeRef(); + + contentClassificationService.classifyContent(LEVEL1, generate(), generate(), newHashSet(REASON), doc1); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase.BehaviourDrivenTest#when() + */ + @Override + public void when() throws Exception + { + doTestInTransaction(new Test() + { + @Override + public Void run() + { + nodeService.setProperty(doc2, PROP_ADDRESSEE, propertyValue); + return null; + } + }, testUser); + + doTestInTransaction(new FailureTest(AccessDeniedException.class) + { + @Override + public void run() throws Exception + { + nodeService.setProperty(doc1, PROP_ADDRESSEE, propertyValue); + } + }, testUser); + +// doTestInTransaction(new FailureTest(AccessDeniedException.class) +// { +// @Override +// public void run() throws Exception +// { +// nodeService.getProperty(doc1, PROP_ADDRESSEE); +// } +// }, testUser); + + doTestInTransaction(new FailureTest(AccessDeniedException.class) + { + @Override + public void run() throws Exception + { + fileFolderService.copy(doc1, folder2, null); + } + }, testUser); + } + }); + } +}