Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java (revision 1859856) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAccessControlManagerTest.java (date 1558688435000) @@ -31,6 +31,7 @@ import org.apache.jackrabbit.oak.plugins.tree.TreeUtil; import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants; import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter; import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal; import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants; import org.jetbrains.annotations.NotNull; @@ -93,7 +94,7 @@ @NotNull private CompositeAccessControlManager createComposite(@NotNull AccessControlManager... acMgrs) { - return new CompositeAccessControlManager(root, NamePathMapper.DEFAULT, getSecurityProvider(), ImmutableList.copyOf(acMgrs)); + return new CompositeAccessControlManager(root, NamePathMapper.DEFAULT, getSecurityProvider(), ImmutableList.copyOf(acMgrs), AggregationFilter.DEFAULT); } @Test Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java (revision 1859856) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeAuthorizationConfigurationTest.java (date 1557900480000) @@ -16,31 +16,42 @@ */ package org.apache.jackrabbit.oak.security.authorization.composite; -import java.security.Principal; -import java.util.Collections; - -import javax.jcr.RepositoryException; -import javax.jcr.security.AccessControlManager; - +import com.google.common.collect.ImmutableSet; import org.apache.jackrabbit.oak.AbstractSecurityTest; +import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.namepath.NamePathMapper; import org.apache.jackrabbit.oak.security.authorization.AuthorizationConfigurationImpl; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; +import org.apache.jackrabbit.oak.spi.security.Context; import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; import org.apache.jackrabbit.oak.spi.security.authorization.OpenAuthorizationConfiguration; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter; import org.apache.jackrabbit.oak.spi.security.authorization.permission.EmptyPermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.restriction.CompositeRestrictionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; +import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal; import org.jetbrains.annotations.NotNull; import org.junit.Test; -import org.mockito.Mockito; + +import javax.jcr.RepositoryException; +import javax.jcr.security.AccessControlManager; +import java.security.Principal; +import java.util.Collections; +import java.util.Set; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; public class CompositeAuthorizationConfigurationTest extends AbstractSecurityTest { @@ -146,8 +157,8 @@ public void testMultipleRestrictionProvider() { // 2 authorization configuration with different RestrictionProvider AuthorizationConfiguration ac = createAuthorizationConfigurationImpl(); - AuthorizationConfiguration ac2 = Mockito.mock(AuthorizationConfiguration.class); - when(ac2.getRestrictionProvider()).thenReturn(Mockito.mock(RestrictionProvider.class)); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); + when(ac2.getRestrictionProvider()).thenReturn(mock(RestrictionProvider.class)); when(ac2.getParameters()).thenReturn(ConfigurationParameters.EMPTY); CompositeAuthorizationConfiguration cc = getCompositeConfiguration(ac, ac2); @@ -160,7 +171,7 @@ public void testRedundantRestrictionProvider() { // 2 authorization configuration sharing the same RestrictionProvider AuthorizationConfiguration ac = createAuthorizationConfigurationImpl(); - AuthorizationConfiguration ac2 = Mockito.mock(AuthorizationConfiguration.class); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); when(ac2.getRestrictionProvider()).thenReturn(ac.getRestrictionProvider()); when(ac2.getParameters()).thenReturn(ConfigurationParameters.EMPTY); @@ -204,4 +215,47 @@ assertFalse(rp instanceof CompositeRestrictionProvider); assertSame(RestrictionProvider.EMPTY, rp); } + + @Test + public void testDefaultEvaluationFilter() { + PermissionProvider pp = mock(PermissionProvider.class, withSettings().extraInterfaces(AggregatedPermissionProvider.class)); + AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); + for (AuthorizationConfiguration ac : new AuthorizationConfiguration[] {ac1, ac2}) { + when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp); + when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY); + when(ac.getContext()).thenReturn(Context.DEFAULT); + } + CompositeAuthorizationConfiguration cc = getCompositeConfiguration(ac1, ac2); + PermissionProvider permissionProvider = cc.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of(EveryonePrincipal.getInstance())); + permissionProvider.refresh(); + + verify(pp, times(2)).refresh(); + } + + @Test + public void testAbortigEvaluationFilter() { + Set principalSet = ImmutableSet.of(EveryonePrincipal.getInstance()); + AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class); + AggregationFilter filter = when(mock(AggregationFilter.class).stop(pp, principalSet)).thenReturn(true).getMock(); + + AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); + for (AuthorizationConfiguration ac : new AuthorizationConfiguration[] {ac1, ac2}) { + when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp); + when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY); + when(ac.getContext()).thenReturn(Context.DEFAULT); + } + CompositeAuthorizationConfiguration cc = getCompositeConfiguration(ac1, ac2); + cc.withAggregationFilter(filter); + + PermissionProvider permissionProvider = cc.getPermissionProvider(root, adminSession.getWorkspaceName(), principalSet); + permissionProvider.refresh(); + + Set nonMatchingSet = adminSession.getAuthInfo().getPrincipals(); + permissionProvider = cc.getPermissionProvider(root, adminSession.getWorkspaceName(), nonMatchingSet); + permissionProvider.refresh(); + + verify(pp, times(3)).refresh(); + } } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java (revision 1859856) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/internal/SecurityProviderRegistrationTest.java (date 1557849263000) @@ -19,8 +19,10 @@ import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import org.apache.jackrabbit.oak.AbstractSecurityTest; +import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.plugins.memory.PropertyStates; import org.apache.jackrabbit.oak.plugins.tree.RootProvider; @@ -48,6 +50,9 @@ import org.apache.jackrabbit.oak.spi.security.authentication.token.CompositeTokenConfiguration; import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration; import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregationFilter; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider; import org.apache.jackrabbit.oak.spi.security.principal.CompositePrincipalConfiguration; import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration; @@ -72,6 +77,7 @@ import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import static org.apache.jackrabbit.oak.spi.security.RegistrationConstants.OAK_SECURITY_NAME; @@ -82,12 +88,15 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; import static org.osgi.framework.Constants.SERVICE_PID; +import static org.osgi.framework.Constants.SERVICE_RANKING; public class SecurityProviderRegistrationTest extends AbstractSecurityTest { @@ -850,4 +859,131 @@ registration.unbindTreeProvider(tp); assertNull(f.get(registration)); } + + @Test + public void testBindEvaluationFilter() { + registration.activate(context.bundleContext(), configWithRequiredServiceIds("filterId", "a1", "a2")); + + AggregationFilter filter = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE)); + ServiceRegistration sr = context.bundleContext().registerService(AggregationFilter.class.getName(), filter, new Hashtable(ImmutableMap.of(SERVICE_PID, "filterId"))); + registration.bindAggregationFilter(sr.getReference(), filter); + + AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class); + AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); + for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) { + when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp); + when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY); + when(ac.getContext()).thenReturn(Context.DEFAULT); + } + + registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "a1"))); + registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "a2"))); + + SecurityProvider service = context.getService(SecurityProvider.class); + + AuthorizationConfiguration ac = service.getConfiguration(AuthorizationConfiguration.class); + assertTrue(ac instanceof CompositeAuthorizationConfiguration); + + PermissionProvider permissionProvider = ac.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of()); + assertSame(pp, permissionProvider); + verify(filter, times(1)).stop(pp, ImmutableSet.of()); + } + + @Test + public void testUnbindEvaluationFilter() { + registration.activate(context.bundleContext(), configWithRequiredServiceIds("a1", "a2", "f1")); + + AggregationFilter filter = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE)); + ServiceRegistration sr = context.bundleContext().registerService(AggregationFilter.class.getName(), filter, new Hashtable(ImmutableMap.of(SERVICE_PID, "f1"))); + registration.bindAggregationFilter(sr.getReference(), filter); + + AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class); + AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); + for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) { + when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp); + when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY); + when(ac.getContext()).thenReturn(Context.DEFAULT); + } + + registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "a1"))); + registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "a2"))); + + AuthorizationConfiguration ac = context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class); + assertTrue(ac instanceof CompositeAuthorizationConfiguration); + + PermissionProvider permissionProvider = ac.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of()); + assertSame(pp, permissionProvider); + verify(filter, times(1)).stop(pp, ImmutableSet.of()); + + registration.unbindAggregationFilter(sr.getReference(), filter); + assertNull(context.getService(SecurityProvider.class)); + + registration.modified(configWithRequiredServiceIds("a1", "a2")); + + context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class).getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of()); + // since unbind was called on filter -> no additional calls + verify(filter, times(1)).stop(pp, ImmutableSet.of()); + } + + @Test + public void testMultipleEvaluationFilterFalse() throws Exception { + registration.activate(context.bundleContext(), configWithRequiredServiceIds("f1", "f2", "ac1", "ac2")); + + AggregationFilter filter1 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.FALSE)); + ServiceRegistration sr1 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter1, new Hashtable(ImmutableMap.of(SERVICE_PID, "f1", SERVICE_RANKING, 100))); + registration.bindAggregationFilter(sr1.getReference(), filter1); + + AggregationFilter filter2 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.FALSE)); + ServiceRegistration sr2 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter2, new Hashtable(ImmutableMap.of(SERVICE_PID, "f2", SERVICE_RANKING, 200))); + registration.bindAggregationFilter(sr2.getReference(), filter2); + + AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class); + AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); + for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) { + when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp); + when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY); + when(ac.getContext()).thenReturn(Context.DEFAULT); + } + registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac1"))); + registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac2"))); + + AuthorizationConfiguration config = context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class); + PermissionProvider permissionProvider = config.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of()); + + verify(filter1, times(2)).stop(pp, ImmutableSet.of()); + verify(filter2, times(2)).stop(pp, ImmutableSet.of()); + } + + @Test + public void testMultipleEvaluationFilterTrue() throws Exception { + registration.activate(context.bundleContext(), configWithRequiredServiceIds("f1", "f2", "ac1", "ac2")); + + AggregationFilter filter1 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE)); + ServiceRegistration sr1 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter1, new Hashtable(ImmutableMap.of(SERVICE_PID, "f1", SERVICE_RANKING, 200))); + registration.bindAggregationFilter(sr1.getReference(), filter1); + + AggregationFilter filter2 = mock(AggregationFilter.class, withSettings().defaultAnswer(invocationOnMock -> Boolean.TRUE)); + ServiceRegistration sr2 = context.bundleContext().registerService(AggregationFilter.class.getName(), filter2, new Hashtable(ImmutableMap.of(SERVICE_PID, "f2", SERVICE_RANKING, 100))); + registration.bindAggregationFilter(sr2.getReference(), filter2); + + AggregatedPermissionProvider pp = mock(AggregatedPermissionProvider.class); + AuthorizationConfiguration ac1 = mock(AuthorizationConfiguration.class); + AuthorizationConfiguration ac2 = mock(AuthorizationConfiguration.class); + for (AuthorizationConfiguration ac : new AuthorizationConfiguration[]{ac1, ac2}) { + when(ac.getPermissionProvider(any(Root.class), anyString(), any(Set.class))).thenReturn(pp); + when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY); + when(ac.getContext()).thenReturn(Context.DEFAULT); + } + registration.bindAuthorizationConfiguration(ac1, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac1"))); + registration.bindAuthorizationConfiguration(ac2, new Hashtable(ImmutableMap.of(SERVICE_PID, "ac2"))); + + AuthorizationConfiguration config = context.getService(SecurityProvider.class).getConfiguration(AuthorizationConfiguration.class); + PermissionProvider permissionProvider = config.getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of()); + + verify(filter1, never()).stop(pp, ImmutableSet.of()); + verify(filter2, times(1)).stop(pp, ImmutableSet.of()); + } } Index: oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java (date 1558687979000) +++ oak-security-spi/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/permission/AggregationFilterDefaultTest.java (date 1558687979000) @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.spi.security.authorization.permission; + +import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager; +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.junit.Test; + +import javax.jcr.security.AccessControlManager; +import java.util.Collections; + +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; + +public class AggregationFilterDefaultTest { + + @Test + public void testStopPermissionProvider() { + assertFalse(AggregationFilter.DEFAULT.stop(mock(AggregatedPermissionProvider.class), Collections.EMPTY_SET)); + } + + @Test + public void testStopEffectivePoliciesByPath() { + assertFalse(AggregationFilter.DEFAULT.stop(mock(AccessControlManager.class), null)); + assertFalse(AggregationFilter.DEFAULT.stop(mock(AccessControlManager.class), PathUtils.ROOT_PATH)); + } + + @Test + public void testStopEffectivePoliciesByPrincipals() { + assertFalse(AggregationFilter.DEFAULT.stop(mock(JackrabbitAccessControlManager.class), Collections.EMPTY_SET)); + } +} \ No newline at end of file