From 7fd2908d1e076310afd07bb6f21b3008ae389e66 Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Wed, 30 Apr 2014 15:58:52 +0000 Subject: [PATCH] Merged HEAD-BUG-FIX (4.3/Cloud) to HEAD (4.3/Cloud) 67735: Merged V4.2-BUG-FIX (4.2.3) to HEAD-BUG-FIX (4.3/Cloud) 65767: Merged DEV to V4.2-BUG-FIX 65559 : MNT-10807 : Auditing does not take into account audit.filter.alfresco-access.transaction.user Added UserAuditFilter class, inject an instance into AuditComponentImpl. Added unit test. 65671 : MNT-10807 : Auditing does not take into account audit.filter.alfresco-access.transaction.user Fixed some code problem. Reverted test from AuditComponentTest and created new UserAuditFilterTest. 65754 : MNT-10807 : Auditing does not take into account audit.filter.alfresco-access.transaction.user Included 'UserAuditFilterTest' test in a test suite. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@68374 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/audit-services-context.xml | 9 ++ .../alfresco/repo/audit/AuditComponent.java | 9 +- .../repo/audit/AuditComponentImpl.java | 9 +- .../alfresco/repo/audit/UserAuditFilter.java | 109 ++++++++++++++ .../repo/audit/AuditComponentTest.java | 2 - .../alfresco/repo/audit/AuditTestSuite.java | 1 + .../repo/audit/UserAuditFilterTest.java | 136 ++++++++++++++++++ 7 files changed, 271 insertions(+), 4 deletions(-) create mode 100644 source/java/org/alfresco/repo/audit/UserAuditFilter.java create mode 100644 source/test-java/org/alfresco/repo/audit/UserAuditFilterTest.java diff --git a/config/alfresco/audit-services-context.xml b/config/alfresco/audit-services-context.xml index 6e9618fdc3..7d5bbdc953 100644 --- a/config/alfresco/audit-services-context.xml +++ b/config/alfresco/audit-services-context.xml @@ -30,6 +30,7 @@ + @@ -37,6 +38,14 @@ + + + + + ${audit.filter.alfresco-access.transaction.user} + + + diff --git a/source/java/org/alfresco/repo/audit/AuditComponent.java b/source/java/org/alfresco/repo/audit/AuditComponent.java index cee7afd3ef..ea9966c9c5 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponent.java +++ b/source/java/org/alfresco/repo/audit/AuditComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2014 Alfresco Software Limited. * * This file is part of Alfresco * @@ -53,6 +53,13 @@ public interface AuditComponent */ public void setAuditEnabled(boolean enable); + /** + * @param userAuditFilter + * + * @since 4.2 + */ + public void setUserAuditFilter(UserAuditFilter userAuditFilter); + /** * Get all registered audit applications, whether active or not. * diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java index 23aa538ecb..57404fd1f5 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -75,6 +75,7 @@ public class AuditComponentImpl implements AuditComponent private AuditDAO auditDAO; private TransactionService transactionService; private AuditFilter auditFilter; + private UserAuditFilter userAuditFilter; /** * Default constructor @@ -126,6 +127,11 @@ public class AuditComponentImpl implements AuditComponent this.auditFilter = auditFilter; } + public void setUserAuditFilter(UserAuditFilter userAuditFilter) + { + this.userAuditFilter = userAuditFilter; + } + /** * {@inheritDoc} * @since 3.2 @@ -499,7 +505,8 @@ public class AuditComponentImpl implements AuditComponent ParameterCheck.mandatory("rootPath", rootPath); AuditApplication.checkPathFormat(rootPath); - if (values == null || values.isEmpty() || !areAuditValuesRequired() || !auditFilter.accept(rootPath, values)) + String username = AuthenticationUtil.getFullyAuthenticatedUser(); + if (values == null || values.isEmpty() || !areAuditValuesRequired() || !userAuditFilter.acceptUser(username) || !auditFilter.accept(rootPath, values)) { return Collections.emptyMap(); } diff --git a/source/java/org/alfresco/repo/audit/UserAuditFilter.java b/source/java/org/alfresco/repo/audit/UserAuditFilter.java new file mode 100644 index 0000000000..a88b0aacee --- /dev/null +++ b/source/java/org/alfresco/repo/audit/UserAuditFilter.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2005-2014 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.repo.audit; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.springframework.beans.factory.InitializingBean; + +public class UserAuditFilter implements InitializingBean +{ + private static final char NOT = '~'; + private static final String REG_EXP_SEPARATOR = ";"; + private static final char ESCAPE = '\\'; + private static final String ESCAPED_NOT = ""+ESCAPE+NOT; + + private String userFilterPattern; + private List> listOfPairValue = new ArrayList>(); + + /** + * Default constructor + */ + public UserAuditFilter() + { + } + + /* + * Set user audit pattern. For example "audit.filter.alfresco-access.transaction.user=~user1;user2;.*" + * + * @param userFilterPattern 'userFilterPattern' is String type. The value of 'userFilterPattern' couldn't empty + * or have 0 length value. An expression that starts with a '~' indicates that any + * matching value should be rejected. Each regular expression in the list is separated + * by a semicolon (';'). + */ + public void setUserFilterPattern(String userFilterPattern) + { + this.userFilterPattern = userFilterPattern; + } + + public void afterPropertiesSet() + { + parseProperties(); + } + + private void parseProperties() + { + String userPropertyValue = userFilterPattern; + if (!PropertyCheck.isValidPropertyString(userPropertyValue)) + { + return; + } + + String[] arrValues = userPropertyValue.split(REG_EXP_SEPARATOR); + for (String prop : arrValues) + { + boolean includeExp = prop.charAt(0) != NOT; + + if (!includeExp || prop.startsWith(ESCAPED_NOT)) + { + prop = prop.substring(1); + } + try + { + listOfPairValue.add(new Pair(includeExp, Pattern.compile(prop))); + } + catch (PatternSyntaxException ex) + { + throw new AlfrescoRuntimeException("The 'audit.filter.alfresco-access.transaction.user' property parse exception; see property 'audit.filter.alfresco-access.transaction.user'.", ex); + } + } + } + + public boolean acceptUser(String value) + { + if (value == null) + { + value = "null"; + } + for (Pair val : listOfPairValue) + { + if (val.getSecond().matcher(value).matches()) + { + return val.getFirst(); + } + } + return true; + } +} diff --git a/source/test-java/org/alfresco/repo/audit/AuditComponentTest.java b/source/test-java/org/alfresco/repo/audit/AuditComponentTest.java index df770ce81a..bbf99f2ca0 100644 --- a/source/test-java/org/alfresco/repo/audit/AuditComponentTest.java +++ b/source/test-java/org/alfresco/repo/audit/AuditComponentTest.java @@ -932,8 +932,6 @@ public class AuditComponentTest extends TestCase AuthenticationUtil.runAs(testRunAs, "SomeOtherUser"); } - - /** * Clearn the audit log as 'admin' */ diff --git a/source/test-java/org/alfresco/repo/audit/AuditTestSuite.java b/source/test-java/org/alfresco/repo/audit/AuditTestSuite.java index 8b7383e4e5..06f0285185 100644 --- a/source/test-java/org/alfresco/repo/audit/AuditTestSuite.java +++ b/source/test-java/org/alfresco/repo/audit/AuditTestSuite.java @@ -40,6 +40,7 @@ public class AuditTestSuite extends TestSuite suite.addTestSuite(AuditableAspectTest.class); suite.addTestSuite(AuditBootstrapTest.class); suite.addTestSuite(AuditComponentTest.class); + suite.addTestSuite(UserAuditFilterTest.class); suite.addTest(new JUnit4TestAdapter(PropertyAuditFilterTest.class)); suite.addTest(new JUnit4TestAdapter(AccessAuditorTest.class)); diff --git a/source/test-java/org/alfresco/repo/audit/UserAuditFilterTest.java b/source/test-java/org/alfresco/repo/audit/UserAuditFilterTest.java new file mode 100644 index 0000000000..8cd3660d4d --- /dev/null +++ b/source/test-java/org/alfresco/repo/audit/UserAuditFilterTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2014 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.repo.audit; + +import java.io.Serializable; +import java.net.URL; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.audit.model.AuditModelRegistryImpl; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; +import org.springframework.util.ResourceUtils; + +/** + * Tests user filter. + * + * @see UserAuditFilter + * + * @author Vasily Olhin + * @since 4.2 + */ +public class UserAuditFilterTest extends TestCase +{ + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private AuditModelRegistryImpl auditModelRegistry; + private AuditComponent auditComponent; + private ServiceRegistry serviceRegistry; + private TransactionService transactionService; + + @Override + public void setUp() throws Exception + { + auditModelRegistry = (AuditModelRegistryImpl) ctx.getBean("auditModel.modelRegistry"); + auditComponent = (AuditComponent) ctx.getBean("auditComponent"); + serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + transactionService = serviceRegistry.getTransactionService(); + + // Register the test model + URL testModelUrl = ResourceUtils.getURL("classpath:alfresco/testaudit/alfresco-audit-test.xml"); + auditModelRegistry.registerModel(testModelUrl); + auditModelRegistry.loadAuditModels(); + } + + @Override + public void tearDown() throws Exception + { + AuthenticationUtil.clearCurrentSecurityContext(); + // Throw away the reconfigured registry state + auditModelRegistry.destroy(); + } + + public void testUserFilter() + { + Map userArr = new HashMap(); + userArr.put(false, "user1"); + userArr.put(true, "user2"); + userArr.put(true, "bob"); + UserAuditFilter userAuditFilter = new UserAuditFilter(); + userAuditFilter.setUserFilterPattern("~user1;user2;.*"); + userAuditFilter.afterPropertiesSet(); + auditComponent.setUserAuditFilter(userAuditFilter); + + final RetryingTransactionCallback> testCallback = new RetryingTransactionCallback>() + { + public Map execute() throws Throwable + { + Map values = new HashMap(13); + values.put("/3.1/4.1", new Long(41)); + values.put("/3.1/4.2", "42"); + values.put("/3.1/4.3", new Date()); + values.put("/3.1/4.4", ""); + values.put("/3.1/4.5", null); + + return auditComponent.recordAuditValues("/test/one.one/two.one", values); + } + }; + RunAsWork> testRunAs = new RunAsWork>() + { + public Map doWork() throws Exception + { + return transactionService.getRetryingTransactionHelper().doInTransaction(testCallback); + } + }; + // record audit values using different users + Map result; + Set> userSet = userArr.entrySet(); + for(Map.Entry entry : userSet) + { + result = AuthenticationUtil.runAs(testRunAs, entry.getValue()); + assertEquals((boolean) entry.getKey(), !result.isEmpty()); + } + } + + public void testUserFilterParseRedirectProperty() + { + UserAuditFilter userAuditFilter = new UserAuditFilter(); + userAuditFilter.setUserFilterPattern("~user1;${audit.test.user};.*"); + try + { + userAuditFilter.afterPropertiesSet(); + fail("UserAuditFilter shouldn't parse property with redirect '$'"); + } + catch (AlfrescoRuntimeException ex) + { + // Expected + } + } +}