| /* |
| * 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.principalbased.impl; |
| |
| import org.apache.jackrabbit.guava.common.collect.ImmutableSet; |
| import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal; |
| import org.apache.jackrabbit.api.security.user.User; |
| import org.apache.jackrabbit.oak.commons.PathUtils; |
| import org.apache.jackrabbit.oak.namepath.NamePathMapper; |
| import org.apache.jackrabbit.oak.spi.security.SecurityProvider; |
| import org.apache.jackrabbit.oak.spi.security.authorization.principalbased.Filter; |
| import org.apache.jackrabbit.oak.spi.security.authorization.principalbased.FilterProvider; |
| import org.apache.jackrabbit.oak.spi.security.principal.AdminPrincipal; |
| import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration; |
| import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider; |
| import org.apache.jackrabbit.oak.spi.security.principal.SystemUserPrincipal; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import javax.jcr.RepositoryException; |
| import java.security.Principal; |
| import java.util.Collections; |
| import java.util.Set; |
| |
| import static java.util.Collections.singleton; |
| import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.DEFAULT_SYSTEM_RELATIVE_PATH; |
| import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.DEFAULT_USER_PATH; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| 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; |
| |
| public class FilterImplTest extends AbstractPrincipalBasedTest { |
| |
| private Filter filter; |
| private String supportedPath; |
| |
| @Before |
| public void before() throws Exception { |
| super.before(); |
| FilterProvider fp = getFilterProvider(); |
| filter = fp.getFilter(getSecurityProvider(), root, getNamePathMapper()); |
| supportedPath = fp.getFilterRoot(); |
| } |
| |
| @Test |
| public void testCanHandleEmptySet() { |
| assertFalse(filter.canHandle(Collections.emptySet())); |
| } |
| |
| @Test |
| public void testCanHandleGroupPrincipal() throws Exception { |
| assertFalse(filter.canHandle(singleton(getUserManager(root).createGroup("group").getPrincipal()))); |
| } |
| |
| @Test |
| public void testCanHandleUserPrincipal() throws Exception { |
| assertFalse(filter.canHandle(singleton(getTestUser().getPrincipal()))); |
| } |
| |
| @Test |
| public void testCanHandleUnknownSystemUserPrincipal() { |
| SystemUserPrincipal principal = () -> "systemUserPrincipal"; |
| assertFalse(filter.canHandle(singleton(principal))); |
| } |
| |
| @Test |
| public void testCanHandleRandomSystemUserPrincipal() throws Exception { |
| Principal principal = getUserManager(root).createSystemUser("anySystemUser", null).getPrincipal(); |
| assertFalse(filter.canHandle(singleton(principal))); |
| } |
| |
| @Test |
| public void testCanHandleValidSystemUserPrincipal() throws Exception { |
| assertTrue(filter.canHandle(singleton(getTestSystemUser().getPrincipal()))); |
| } |
| |
| @Test |
| public void testCanHandleValidSystemUserPrincipal2() throws Exception { |
| Principal principal = getTestSystemUser().getPrincipal(); |
| assertTrue(filter.canHandle(singleton((SystemUserPrincipal) () -> principal.getName()))); |
| } |
| |
| @Test |
| public void testCanHandleWrongPrincipalClass() throws Exception { |
| Principal principal = getTestSystemUser().getPrincipal(); |
| assertFalse(filter.canHandle(singleton((AdminPrincipal) () -> principal.getName()))); |
| assertFalse(filter.canHandle(singleton((new ItemBasedPrincipal() { |
| @NotNull |
| @Override |
| public String getPath() throws RepositoryException { |
| return ((ItemBasedPrincipal) principal).getPath(); |
| } |
| |
| @Override |
| public String getName() { |
| return principal.getName(); |
| } |
| })))); |
| } |
| |
| @Test |
| public void testCanHandleItemBasedSystemUserPrincipalUnsupportedPath() throws Exception { |
| // make sure supported path exists |
| User tu = getTestSystemUser(); |
| assertTrue(root.getTree(supportedPath).exists()); |
| Principal principal = new TestPrincipal("name", PathUtils.getParentPath(supportedPath)); |
| assertFalse(filter.canHandle(singleton(principal))); |
| } |
| |
| @Test |
| public void testCanHandleMovedItemBasedSystemUserPrincipal() throws Exception { |
| Principal principal = getTestSystemUser().getPrincipal(); |
| assertTrue(filter.canHandle(singleton(principal))); |
| |
| String oakPath = filter.getOakPath(principal); |
| assertEquals(getNamePathMapper().getOakPath(getTestSystemUser().getPath()), oakPath); |
| |
| String destPath = oakPath + "_moved"; |
| root.move(oakPath, destPath); |
| |
| Principal movedPrincipal = new TestPrincipal(principal.getName(), destPath); |
| assertTrue(filter.canHandle(singleton(movedPrincipal))); |
| assertEquals(destPath, filter.getOakPath(movedPrincipal)); |
| } |
| |
| @Test |
| public void testCanHandleMovedUnsupportedItemBasedSystemUserPrincipal() throws Exception { |
| Principal principal = getTestSystemUser().getPrincipal(); |
| String srcPath = getNamePathMapper().getOakPath(getTestSystemUser().getPath()); |
| String destPath = PathUtils.concat(DEFAULT_USER_PATH, DEFAULT_SYSTEM_RELATIVE_PATH, "moved"); |
| Principal movedPrincipal = new TestPrincipal(principal.getName(), destPath); |
| |
| root.move(srcPath, destPath); |
| assertFalse(filter.canHandle(singleton(movedPrincipal))); |
| |
| root.move(destPath, srcPath); |
| assertTrue(filter.canHandle(singleton(principal))); |
| assertEquals(srcPath, filter.getOakPath(movedPrincipal)); |
| } |
| |
| @Test |
| public void testCanHandleRemoved() throws Exception { |
| Principal principal = getTestSystemUser().getPrincipal(); |
| assertTrue(filter.canHandle(singleton(principal))); |
| root.getTree(filter.getOakPath(principal)).remove(); |
| |
| // using original principal again -> obtaining from cache without further validation |
| assertTrue(filter.canHandle(singleton(principal))); |
| // using SystemUserPrincipal without path -> obtaining from cache without further validation |
| assertTrue(filter.canHandle(singleton((SystemUserPrincipal) () -> principal.getName()))); |
| |
| // path mismatch -> lookup will reveal removed tree and adjust caches |
| assertFalse(filter.canHandle(singleton(new TestPrincipal(principal.getName(), null)))); |
| // using original principal will no longer work |
| assertFalse(filter.canHandle(singleton(principal))); |
| } |
| |
| @Test |
| public void testCanHandleGetPathThrows() { |
| Principal principal = new TestPrincipal("name", null); |
| assertFalse(filter.canHandle(singleton(principal))); |
| } |
| |
| /** |
| * Test that the filter can deal with principals that have been accessed with a different {@code NamePathMapper}. |
| * This might occur with {@code AbstractAccessControlManager#hasPrivilege} and {@code AbstractAccessControlManager#getPrivileges}, |
| * when a {@code PermissionProvider} is built from the principal set passed to the Jackrabbit API methods (and not from |
| * principals obtained on the system level when populating the {@code Subject}. |
| */ |
| @Test |
| public void testCanHandlePathMapperMismatch() throws Exception { |
| Principal principal = getTestSystemUser().getPrincipal(); |
| |
| // create filter with a different NamePathMapper than was used to build the principal |
| Filter f = getFilterProvider().getFilter(getSecurityProvider(), root, NamePathMapper.DEFAULT); |
| assertTrue(f.canHandle(singleton(principal))); |
| } |
| |
| @Test |
| public void testCanHandlePathMapperMismatchUnknownPrincipal() { |
| Principal principal = new TestPrincipal("name", PathUtils.concat(supportedPath, "oak:path/to/oak:principal")); |
| |
| // create filter with a different NamePathMapper than was used to build the principal |
| // since the principal is not known to the PrincipalManager, the extra lookup doesn't reveal a valid principal. |
| Filter f = getFilterProvider().getFilter(getSecurityProvider(), root, NamePathMapper.DEFAULT); |
| assertFalse(f.canHandle(singleton(principal))); |
| } |
| |
| @Test |
| public void testCanHandleCombination() throws Exception { |
| assertFalse(filter.canHandle(ImmutableSet.of(getTestSystemUser().getPrincipal(), getTestUser().getPrincipal()))); |
| } |
| |
| @Test |
| public void testCanHandlePopulatesCache() throws Exception { |
| Principal principal = getTestSystemUser().getPrincipal(); |
| PrincipalProvider pp = when(mock(PrincipalProvider.class).getPrincipal(principal.getName())).thenReturn(principal).getMock(); |
| PrincipalConfiguration pc = when(mock(PrincipalConfiguration.class).getPrincipalProvider(root, getNamePathMapper())).thenReturn(pp).getMock(); |
| SecurityProvider sp = when(mock(SecurityProvider.class).getConfiguration(PrincipalConfiguration.class)).thenReturn(pc).getMock(); |
| |
| Filter filter = getFilterProvider().getFilter(sp, root, getNamePathMapper()); |
| |
| // call 'canHandle' twice |
| Set<Principal> set = singleton((SystemUserPrincipal) () -> principal.getName()); |
| assertTrue(filter.canHandle(set)); |
| assertTrue(filter.canHandle(set)); |
| |
| // principalprovider must only be hit once |
| verify(pp, times(1)).getPrincipal(principal.getName()); |
| } |
| |
| @Test |
| public void testCanHandlePopulatesCacheUnsupportedSystemUser() { |
| Principal unsupported = (SystemUserPrincipal) () -> "unsupported"; |
| |
| PrincipalProvider pp = when(mock(PrincipalProvider.class).getPrincipal(unsupported.getName())).thenReturn(unsupported).getMock(); |
| PrincipalConfiguration pc = when(mock(PrincipalConfiguration.class).getPrincipalProvider(root, getNamePathMapper())).thenReturn(pp).getMock(); |
| SecurityProvider sp = when(mock(SecurityProvider.class).getConfiguration(PrincipalConfiguration.class)).thenReturn(pc).getMock(); |
| |
| Filter filter = getFilterProvider().getFilter(sp, root, getNamePathMapper()); |
| |
| // call 'canHandle' twice |
| Set<Principal> set = singleton(unsupported); |
| assertFalse(filter.canHandle(set)); |
| assertFalse(filter.canHandle(set)); |
| |
| // principalprovider must only be hit once |
| verify(pp, times(1)).getPrincipal(unsupported.getName()); |
| } |
| |
| @Test |
| public void testCanHandleUserPrincipalDoesntHitProvider() throws Exception { |
| Principal userPrincipal = getTestUser().getPrincipal(); |
| |
| PrincipalProvider pp = when(mock(PrincipalProvider.class).getPrincipal(userPrincipal.getName())).thenReturn(userPrincipal).getMock(); |
| PrincipalConfiguration pc = when(mock(PrincipalConfiguration.class).getPrincipalProvider(root, getNamePathMapper())).thenReturn(pp).getMock(); |
| SecurityProvider sp = when(mock(SecurityProvider.class).getConfiguration(PrincipalConfiguration.class)).thenReturn(pc).getMock(); |
| |
| Filter filter = getFilterProvider().getFilter(sp, root, getNamePathMapper()); |
| |
| // call 'canHandle' twice |
| Set<Principal> set = singleton(userPrincipal); |
| assertFalse(filter.canHandle(set)); |
| assertFalse(filter.canHandle(set)); |
| |
| // for a non-system-user principal the principal resolution must never be triggered |
| verify(pp, never()).getPrincipal(userPrincipal.getName()); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void testGetPathUserPrincipal() throws Exception { |
| filter.getOakPath(getTestUser().getPrincipal()); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void testGetPathInvalidSystemUserPrincipal() { |
| filter.getOakPath((SystemUserPrincipal) () -> "name"); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void testGetPathValidSystemUserPrincipalNotValidated() throws Exception { |
| filter.getOakPath(getTestSystemUser().getPrincipal()); |
| } |
| |
| @Test |
| public void testGetPathValidatedSystemUserPrincipal() throws Exception { |
| ItemBasedPrincipal principal = (ItemBasedPrincipal) getTestSystemUser().getPrincipal(); |
| filter.canHandle(singleton(principal)); |
| |
| assertNotEquals(principal.getPath(), filter.getOakPath(principal)); |
| assertEquals(getNamePathMapper().getOakPath(principal.getPath()), filter.getOakPath(principal)); |
| } |
| |
| @Test |
| public void testGetPathAfterGetValidUserPrincipal() throws Exception { |
| ItemBasedPrincipal principal = (ItemBasedPrincipal) getTestSystemUser().getPrincipal(); |
| filter.getValidPrincipal(getNamePathMapper().getOakPath(principal.getPath())); |
| |
| assertNotEquals(principal.getPath(), filter.getOakPath(principal)); |
| assertEquals(getNamePathMapper().getOakPath(principal.getPath()), filter.getOakPath(principal)); |
| } |
| |
| @Test |
| public void testGetPrincipalUserPath() throws Exception { |
| assertNull(filter.getValidPrincipal(getNamePathMapper().getOakPath(getTestUser().getPath()))); |
| } |
| |
| @Test |
| public void testGetPrincipalJcrPath() throws Exception { |
| assertNull(filter.getValidPrincipal(getTestSystemUser().getPath())); |
| } |
| |
| @Test |
| public void testGetPrincipalSystemUserPath() throws Exception { |
| User user = getTestSystemUser(); |
| Principal principal = user.getPrincipal(); |
| assertEquals(principal, filter.getValidPrincipal(getNamePathMapper().getOakPath(user.getPath()))); |
| } |
| |
| @Test |
| public void testGetPrincipalSupportedRootPath() { |
| assertNull(filter.getValidPrincipal(supportedPath)); |
| } |
| |
| @Test |
| public void testGetPrincipalMockedItemBasedProvider() throws Exception { |
| ItemBasedPrincipal principal = (ItemBasedPrincipal) getTestSystemUser().getPrincipal(); |
| String oakPath = getNamePathMapper().getOakPath(principal.getPath()); |
| |
| PrincipalProvider pp = when(mock(PrincipalProvider.class).getItemBasedPrincipal(oakPath)).thenReturn(principal).getMock(); |
| PrincipalConfiguration pc = when(mock(PrincipalConfiguration.class).getPrincipalProvider(root, getNamePathMapper())).thenReturn(pp).getMock(); |
| SecurityProvider sp = when(mock(SecurityProvider.class).getConfiguration(PrincipalConfiguration.class)).thenReturn(pc).getMock(); |
| |
| Filter filter = getFilterProvider().getFilter(sp, root, getNamePathMapper()); |
| |
| // call 'getValidPrincipal' twice |
| Principal p = filter.getValidPrincipal(oakPath); |
| assertEquals(principal, p); |
| assertEquals(principal.getPath(), ((ItemBasedPrincipal) p).getPath()); |
| assertEquals(principal, filter.getValidPrincipal(oakPath)); |
| |
| verify(pp, times(2)).getItemBasedPrincipal(oakPath); |
| verify(pp, never()).getPrincipal(principal.getName()); |
| } |
| |
| @Test |
| public void testGetPrincipalMockedPrincipalProvider() throws Exception { |
| ItemBasedPrincipal principal = (ItemBasedPrincipal) getTestSystemUser().getPrincipal(); |
| String oakPath = getNamePathMapper().getOakPath(principal.getPath()); |
| |
| PrincipalProvider pp = mock(PrincipalProvider.class); |
| PrincipalConfiguration pc = when(mock(PrincipalConfiguration.class).getPrincipalProvider(root, getNamePathMapper())).thenReturn(pp).getMock(); |
| SecurityProvider sp = when(mock(SecurityProvider.class).getConfiguration(PrincipalConfiguration.class)).thenReturn(pc).getMock(); |
| |
| Filter filter = getFilterProvider().getFilter(sp, root, getNamePathMapper()); |
| |
| assertNull(filter.getValidPrincipal(oakPath)); |
| verify(pp, never()).getPrincipal(principal.getName()); |
| } |
| |
| private final class TestPrincipal implements SystemUserPrincipal, ItemBasedPrincipal { |
| |
| private final String jcrPath; |
| private final String name; |
| |
| private TestPrincipal(@NotNull String name, @Nullable String oakPath) { |
| if (oakPath == null) { |
| jcrPath = null; |
| } else { |
| jcrPath = getNamePathMapper().getJcrPath(oakPath); |
| assertNotNull(jcrPath); |
| } |
| this.name = name; |
| } |
| |
| @NotNull |
| @Override |
| public String getPath() throws RepositoryException { |
| if (jcrPath != null) { |
| return jcrPath; |
| } else { |
| throw new RepositoryException(); |
| } |
| } |
| |
| @Override |
| public String getName() { |
| return name; |
| } |
| } |
| } |