From 2a398aab124126df06cf787fa474f68527d1f7fe Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Mon, 1 Jun 2015 08:20:12 +0000 Subject: [PATCH] First pass at classification interceptor git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/DEV/ENFORCE@105194 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../classified-content-context.xml | 20 +- .../org_alfresco_module_rm/module-context.xml | 8 +- .../rm-action-context.xml | 1 + .../capability/RMActionProxyFactoryBean.java | 27 ++- .../ClassificationLevelManager.java | 10 +- .../ClassificationServiceBootstrap.java | 8 + .../ClassificationServiceImpl.java | 1 + .../SecurityClearanceServiceImpl.java | 3 +- .../ClassificationMethodInterceptor.java | 180 ++++++++++++++++++ ...icationMethodInterceptorPostProcessor.java | 82 ++++++++ .../util/AlfrescoTransactionSupport.java | 9 + .../EnforceClassificationTest.java | 111 +++++++++++ 12 files changed, 445 insertions(+), 15 deletions(-) create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptor.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptorPostProcessor.java create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/EnforceClassificationTest.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 c07153d8e1..0e94fc654b 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 @@ -1,7 +1,12 @@ - - - + @@ -27,6 +32,15 @@ + + + + + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 2a54c34b40..2a5c2b8cfd 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -1,7 +1,9 @@ - - + @@ -252,6 +254,6 @@ - + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml index 8aeaa34a0a..f3d6f08996 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml @@ -149,6 +149,7 @@ + org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java index ed2a64e6e3..e063372f61 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMActionProxyFactoryBean.java @@ -23,6 +23,8 @@ import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction import org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService; import org.alfresco.repo.action.RuntimeActionService; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.transaction.TransactionService; import org.springframework.aop.framework.ProxyFactoryBean; /** @@ -42,6 +44,9 @@ public class RMActionProxyFactoryBean extends ProxyFactoryBean /** Records management audit service */ protected RecordsManagementAuditService recordsManagementAuditService; + + /** transaction service */ + private TransactionService transactionService; /** * Set action service @@ -72,6 +77,15 @@ public class RMActionProxyFactoryBean extends ProxyFactoryBean { this.recordsManagementAuditService = recordsManagementAuditService; } + + /** + * @param transactionService transaction service + * @since 3.0.a + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } /** * Register the action @@ -82,9 +96,16 @@ public class RMActionProxyFactoryBean extends ProxyFactoryBean { public Void doWork() { - RecordsManagementAction action = (RecordsManagementAction)getObject(); - recordsManagementActionService.register(action); - + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + RecordsManagementAction action = (RecordsManagementAction)getObject(); + recordsManagementActionService.register(action); + return null; + } + }); + return null; } }, AuthenticationUtil.getSystemUserName()); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationLevelManager.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationLevelManager.java index 26f92dce3a..19dd97874a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationLevelManager.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationLevelManager.java @@ -31,11 +31,11 @@ import org.alfresco.module.org_alfresco_module_rm.classification.ClassificationS */ public class ClassificationLevelManager { - /** 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); - + /** 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); + /** An immutable list of classification levels ordered from most to least secure. */ private ImmutableList classificationLevels; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java index 42c3782823..6af2762c14 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceBootstrap.java @@ -56,6 +56,8 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem /** The clearance levels currently configured in this server. */ private ClearanceLevelManager clearanceLevelManager = new ClearanceLevelManager(); private ClassificationServiceDAO classificationServiceDAO; + + private boolean isInitialised = false; public ClassificationServiceBootstrap(AuthenticationUtil authUtil, TransactionService txService, @@ -77,6 +79,11 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem public ClassificationLevelManager getClassificationLevelManager() { return classificationLevelManager; } public ClassificationReasonManager getClassificationReasonManager() { return classificationReasonManager; } public ClearanceLevelManager getClearanceLevelManager() { return clearanceLevelManager; } + + public boolean isInitialised() + { + return isInitialised; + } @Override public void onBootstrap(ApplicationEvent event) { @@ -91,6 +98,7 @@ public class ClassificationServiceBootstrap extends AbstractLifecycleBean implem initConfiguredClassificationLevels(); initConfiguredClassificationReasons(); initConfiguredClearanceLevels(classificationLevelManager.getClassificationLevels()); + isInitialised = true; return null; } }; diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java index ec2ce30787..a5af2717c4 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/ClassificationServiceImpl.java @@ -55,6 +55,7 @@ public class ClassificationServiceImpl extends ServiceBaseImpl } /** + * Create a list containing all classification levels up to and including the supplied level. * * @param allLevels The list of all the classification levels starting with the highest security. diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/SecurityClearanceServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/SecurityClearanceServiceImpl.java index 076d0ba220..afe9499269 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/SecurityClearanceServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/SecurityClearanceServiceImpl.java @@ -41,7 +41,7 @@ import org.alfresco.util.ParameterCheck; * @since 3.0 */ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements SecurityClearanceService -{ +{ /** The clearance levels currently configured in this server. */ private ClearanceLevelManager clearanceManager; /** The object containing the {@link ClassificationLevel}s in the system. */ @@ -50,6 +50,7 @@ public class SecurityClearanceServiceImpl extends ServiceBaseImpl implements Sec private ClassificationServiceBootstrap classificationServiceBootstrap; private ClassificationLevelComparator classificationLevelComparator; + /** dependency setters */ public void setClearanceManager(ClearanceLevelManager clearanceManager) { this.clearanceManager = clearanceManager; } public void setClassificationLevelManager(ClassificationLevelManager classificationLevelManager) { this.classificationLevelManager = classificationLevelManager; } public void setPersonService(PersonService service) { this.personService = service; } 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 new file mode 100644 index 0000000000..565727e284 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptor.java @@ -0,0 +1,180 @@ +/* + * 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.classification.interceptor; + +import java.lang.reflect.Method; + +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.module.org_alfresco_module_rm.util.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.GUID; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Classification method interceptor + * + * @author Roy Wetherall + * @since 3.0 + */ +public class ClassificationMethodInterceptor implements MethodInterceptor, ApplicationContextAware +{ + private static final String KEY_PROCESSING = GUID.generate(); + + /** application context */ + private ApplicationContext applicationContext; + + /** + * @param applicationContext application context + * @throws BeansException + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + + protected AuthenticationUtil getAuthenticationUtil() + { + return (AuthenticationUtil)applicationContext.getBean("rm.authenticationUtil"); + } + + /** + * @return {@link ContentClassificationService} content classification service + */ + protected ContentClassificationService getContentClassificaitonService() + { + 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"); + } + + protected NodeService getNodeService() + { + return (NodeService)applicationContext.getBean("dbNodeService"); + } + + /** + * Check that the current user is cleared to see the items passed as parameters to the current + * method invocation. + * + * @param invocation method invocation + */ + @SuppressWarnings("rawtypes") + public void checkClassification(MethodInvocation invocation) + { + // do in transaction + getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // ensure classification service has been bootstrapped + if (getClassificationServiceBootstrap().isInitialised()) + { + // check that we are not already processing a classification check + Object value = getAlfrescoTransactionSupport().getResource(KEY_PROCESSING); + if (value == null) + { + // check that we have an authenticated user and that they aren't "system" + if (getAuthenticationUtil().getFullyAuthenticatedUser() != null && + !getAuthenticationUtil().isRunAsUserTheSystemUser()) + { + 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, Boolean.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 + if (getNodeService().exists(testNodeRef) && + !getContentClassificaitonService().hasClearance(testNodeRef)) + { + // throw exception + throw new AccessDeniedException("You do not have clearance!"); + } + } + finally + { + // clear the transaction as processed a classification check + getAlfrescoTransactionSupport().unbindResource(KEY_PROCESSING); + } + } + + position++; + } + } + } + } + + return null; + } + }, true); + } + + /** + * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) + */ + @Override + public Object invoke(MethodInvocation invocation) throws Throwable + { + // pre method invocation check + checkClassification(invocation); + + // method proceed + Object result = invocation.proceed(); + + // post method invocation processing + // TODO + + return result; + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptorPostProcessor.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptorPostProcessor.java new file mode 100644 index 0000000000..010d2e98cd --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/classification/interceptor/ClassificationMethodInterceptorPostProcessor.java @@ -0,0 +1,82 @@ +/* + * 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.classification.interceptor; + +import org.springframework.beans.BeansException; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.RuntimeBeanNameReference; +import org.springframework.beans.factory.support.ManagedList; + +/** + * Classification method interceptor bean factory post processor. + *

+ * Bean factory post processor that inspects available beans and adds the classification method interceptor + * to all public services. + * + * @author Roy Wetherall + * @since 3.0.a + */ +public class ClassificationMethodInterceptorPostProcessor implements BeanFactoryPostProcessor +{ + private static final String PROP_INTERCEPTOR_NAMES = "interceptorNames"; + private static final String TYPE_PROXY_FACTORY_BEAN = "ProxyFactoryBean"; + private static final String POSTFIX_SERVICE = "Service"; + private static final String BEAN_NAME_CLASSIFICATION_METHOD_INTERCEPTOR = "classificationMethodInterceptor"; + + /** + * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) + */ + @SuppressWarnings("unchecked") + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + // get all bean definition names + String beans[] = beanFactory.getBeanDefinitionNames(); + for (String bean : beans) + { + // get bean definition + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(bean); + + // only modify proxy factory beans that follow the public service naming postfix convention + if (beanDefinition.getBeanClassName() != null && + beanDefinition.getBeanClassName().endsWith(TYPE_PROXY_FACTORY_BEAN) && + bean.endsWith(POSTFIX_SERVICE)) + { + // get the property values for the bean definition + MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); + if (propertyValues.contains(PROP_INTERCEPTOR_NAMES)) + { + // get the current list of interceptor names + PropertyValue value = propertyValues.getPropertyValue(PROP_INTERCEPTOR_NAMES); + ManagedList list = (ManagedList)value.getValue(); + if (!list.isEmpty()) + { + // add reference to classification method interceptor + RuntimeBeanNameReference beanReference = new RuntimeBeanNameReference(BEAN_NAME_CLASSIFICATION_METHOD_INTERCEPTOR); + list.add(beanReference); + } + } + } + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AlfrescoTransactionSupport.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AlfrescoTransactionSupport.java index d2f9796300..4e30188a60 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AlfrescoTransactionSupport.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AlfrescoTransactionSupport.java @@ -42,4 +42,13 @@ public class AlfrescoTransactionSupport { org.alfresco.repo.transaction.AlfrescoTransactionSupport.unbindResource(key); } + + /** + * @see org.alfresco.repo.transaction.AlfrescoTransactionSupport#getResource(Object) + * @since 3.0.a + */ + public Object getResource(Object key) + { + return org.alfresco.repo.transaction.AlfrescoTransactionSupport.getResource(key); + } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/EnforceClassificationTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/EnforceClassificationTest.java new file mode 100644 index 0000000000..2898124ee5 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/classification/EnforceClassificationTest.java @@ -0,0 +1,111 @@ +/* + * 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; + +import java.util.Collections; +import java.util.List; + +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.test.util.BaseRMTestCase; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.security.permissions.impl.model.PermissionModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.util.GUID; + +/** + * Enforce classification integration test + * + * @author Roy Wetherall + * @since 3.0 + */ +public class EnforceClassificationTest extends BaseRMTestCase +{ + /** test data */ + private static final String CLASSIFICATION_LEVEL1 = "level1"; + private static final String CLASSIFICATION_LEVEL2 = "level2"; + private static final String CLASSIFICATION_LEVEL3 = "level3"; + private static final String CLASSIFICATION_LEVEL4 = "level4"; + + private static final String CLASSIFICATION_REASON = "Test Reason 1"; + private static final String CLASSIFICATION_AUTHORITY = "classification.authority"; + private static final String RECORD_NAME = "recordname.txt"; + + private ContentClassificationService contentClassificationService; + + @Override + protected void initServices() + { + super.initServices(); + contentClassificationService = (ContentClassificationService)applicationContext.getBean("contentClassificationService"); + } + + @Override + protected boolean isCollaborationSiteTest() + { + return true; + } + + /** + * + */ + public void testUserNotClearedDocument() throws Exception + { + doBehaviourDrivenTest(new BehaviourDrivenTest(AccessDeniedException.class) + { + private String userName; + + public void given() throws Exception + { + // create test person and assign read permission to document + userName = GUID.generate(); + createPerson(userName, true); + permissionService.setPermission(dmDocument, userName , PermissionService.READ, true); + + // assign security clearance + securityClearanceService.setUserSecurityClearance(userName, CLASSIFICATION_LEVEL3); + + // classify document + contentClassificationService.classifyContent( + CLASSIFICATION_LEVEL1, + CLASSIFICATION_AUTHORITY, + Collections.singleton(CLASSIFICATION_REASON), + dmDocument); + + } + + public void when() throws Exception + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Void doWork() throws Exception + { + nodeService.getAspects(dmDocument); + + return null; + } + }, userName); + } + }); + } +}