| /* |
| * 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.sling.feature.cpconverter.accesscontrol; |
| |
| import org.apache.jackrabbit.vault.util.PlatformNameFormat; |
| import org.apache.sling.feature.ArtifactId; |
| import org.apache.sling.feature.Extension; |
| import org.apache.sling.feature.Feature; |
| import org.apache.sling.feature.cpconverter.Util; |
| import org.apache.sling.feature.cpconverter.features.DefaultFeaturesManager; |
| import org.apache.sling.feature.cpconverter.features.FeaturesManager; |
| import org.apache.sling.feature.cpconverter.shared.RepoPath; |
| import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler; |
| import org.apache.sling.repoinit.parser.RepoInitParser; |
| import org.apache.sling.repoinit.parser.RepoInitParsingException; |
| import org.apache.sling.repoinit.parser.impl.RepoInitParserService; |
| import org.apache.sling.repoinit.parser.operations.Operation; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.mockito.Mockito; |
| |
| import java.io.File; |
| import java.io.StringReader; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.verifyNoInteractions; |
| import static org.mockito.Mockito.when; |
| |
| public class AclManagerTest { |
| private AclManager aclManager; |
| private Path tempDir; |
| |
| @Before |
| public void setUp() throws Exception { |
| aclManager = new DefaultAclManager(); |
| tempDir = Files.createTempDirectory(getClass().getSimpleName()); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| aclManager = null; |
| |
| // Delete the temp dir again |
| Files.walk(tempDir) |
| .sorted(Comparator.reverseOrder()) |
| .map(Path::toFile) |
| .forEach(File::delete); |
| } |
| |
| @Test |
| public void makeSureAclsAreCreatedOnlyoutsideSytemUsersPaths() throws Exception { |
| aclManager.addSystemUser(new SystemUser("acs-commons-package-replication-status-event-service", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| aclManager.addAcl("acs-commons-package-replication-status-event-service", newAcl(true, "jcr:read,rep:write,rep:indexDefinitionManagement", "/_sling_tests/not/system/user/path")); |
| aclManager.addAcl("acs-commons-package-replication-status-event-service", newAcl(true, "jcr:read,crx:replicate,jcr:removeNode", "/home/users/system")); |
| |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(tempDir.toFile()); |
| when(assembler.getEntry("_sling_tests/not/.content.xml")).thenReturn(new File(getClass().getResource("_sling_tests/not/.content.xml").getFile())); |
| |
| |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| assertNotNull(repoinitExtension); |
| |
| // acs-commons-on-deploy-scripts-service will be missed |
| String expected = Util.normalize( |
| "create service user acs-commons-package-replication-status-event-service with path system\n" + |
| "create path /sling:tests/not(nt:unstructured mixin rep:AccessControllable,mix:created)/system/user/path\n" + |
| "set ACL for acs-commons-package-replication-status-event-service\n" + |
| " allow jcr:read,rep:write,rep:indexDefinitionManagement on /sling:tests/not/system/user/path\n" + |
| " allow jcr:read,crx:replicate,jcr:removeNode on /home/users/system\n" + |
| "end\n"); |
| String actual = repoinitExtension.getText(); |
| assertEquals(expected, actual); |
| |
| RepoInitParser repoInitParser = new RepoInitParserService(); |
| List<Operation> operations = repoInitParser.parse(new StringReader(actual)); |
| assertFalse(operations.isEmpty()); |
| } |
| |
| @Test |
| public void testReset() throws RepoInitParsingException { |
| // We assume this user will not be in the result because of the reset in the next line |
| aclManager.addSystemUser(new SystemUser("acs-commons-ensure-oak-index-service", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| // emulate a second iteration of conversion |
| aclManager.reset(); |
| |
| aclManager.addSystemUser(new SystemUser("acs-commons-package-replication-status-event-service", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| aclManager.addAcl("acs-commons-package-replication-status-event-service", newAcl(true, "jcr:read,rep:write,rep:indexDefinitionManagement", "/_sling_tests/not/system/user/path")); |
| |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(tempDir.toFile()); |
| when(assembler.getEntry("_sling_tests/not/.content.xml")).thenReturn(new File(getClass().getResource("_sling_tests/not/.content.xml").getFile())); |
| |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| assertNotNull(repoinitExtension); |
| |
| // aacs-commons-ensure-oak-index-service will be missed |
| String expected = Util.normalize( |
| "create service user acs-commons-package-replication-status-event-service with path system\n" + |
| "create path /sling:tests/not(nt:unstructured mixin rep:AccessControllable,mix:created)/system/user/path\n" + |
| "set ACL for acs-commons-package-replication-status-event-service\n" + |
| " allow jcr:read,rep:write,rep:indexDefinitionManagement on /sling:tests/not/system/user/path\n" + |
| "end\n"); |
| String actual = repoinitExtension.getText(); |
| assertEquals(expected, actual); |
| |
| RepoInitParser repoInitParser = new RepoInitParserService(); |
| List<Operation> operations = repoInitParser.parse(new StringReader(actual)); |
| assertFalse(operations.isEmpty()); |
| } |
| |
| @Test |
| public void testAddACLforUnknownUser() throws RepoInitParsingException { |
| // we expect this acl to not show up because the user is unknown |
| aclManager.addAcl("acs-commons-on-deploy-scripts-service", newAcl(true, "jcr:read,crx:replicate,jcr:removeNode", "/home/users/system")); |
| |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| assertNull(repoinitExtension); |
| } |
| |
| @Test |
| public void pathWithSpecialCharactersTest() throws RepoInitParsingException { |
| aclManager.addSystemUser(new SystemUser("sys-usr", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:read", "/content/_cq_tags")); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:write", "/content/cq:tags")); |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| assertNotNull(repoinitExtension); |
| |
| String expected = Util.normalize( |
| "create service user sys-usr with path system\n" + |
| "create path /content/cq:tags\n"+ |
| "set ACL for sys-usr\n" + |
| " allow jcr:read on /content/cq:tags\n" + |
| " allow jcr:write on /content/cq:tags\n" + |
| "end\n"); |
| |
| String actual = repoinitExtension.getText(); |
| assertEquals(expected, actual); |
| |
| RepoInitParser repoInitParser = new RepoInitParserService(); |
| List<Operation> operations = repoInitParser.parse(new StringReader(actual)); |
| assertFalse(operations.isEmpty()); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testGroupHandlingWithGroupUsed() { |
| aclManager.addSystemUser(new SystemUser("sys-usr", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| aclManager.addGroup(new Group("test", new RepoPath("/home/groups/test"), new RepoPath("/home/groups/test"))); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:read", "/home/groups/test")); |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| } |
| |
| @Test |
| public void testGroupHandlingWithGroupNotUsed() throws RepoInitParsingException { |
| aclManager.addSystemUser(new SystemUser("sys-usr", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| aclManager.addGroup(new Group("test", new RepoPath("/home/groups/test"), new RepoPath("/home/groups/test"))); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:read", "/content/test")); |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| assertNotNull(repoinitExtension); |
| |
| String expected = Util.normalize( |
| "create service user sys-usr with path system\n" + |
| "create path /content/test\n" + |
| "set ACL for sys-usr\n" + |
| " allow jcr:read on /content/test\n" + |
| "end\n"); |
| |
| String actual = repoinitExtension.getText(); |
| assertEquals(expected, actual); |
| |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testGroupHandlingWithGroupMatchingSubPath() { |
| aclManager.addSystemUser(new SystemUser("sys-usr", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| aclManager.addGroup(new Group("test", new RepoPath("/home/groups/test"), new RepoPath("/home/groups/test"))); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:read", "/home/groups/test/foo")); |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testUserHandlingWithMatchingUser() { |
| aclManager.addSystemUser(new SystemUser("sys-usr", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| aclManager.addUser(new User("test", new RepoPath("/home/users/test"), new RepoPath("/home/users/test"))); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:read", "/home/users/test/foo")); |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| } |
| |
| @Test |
| public void testUserHandlingWithNonMatchingUser() throws RepoInitParsingException { |
| aclManager.addSystemUser(new SystemUser("sys-usr", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| aclManager.addUser(new User("test", new RepoPath("/home/users/test"), new RepoPath("/home/users/test"))); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:read", "/content/test")); |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| assertNotNull(repoinitExtension); |
| |
| String expected = Util.normalize( |
| "create service user sys-usr with path system\n" + |
| "create path /content/test\n" + |
| "set ACL for sys-usr\n" + |
| " allow jcr:read on /content/test\n" + |
| "end\n"); |
| |
| String actual = repoinitExtension.getText(); |
| assertEquals(expected, actual); |
| } |
| |
| @Test |
| public void testPathHandlingWithUser() throws RepoInitParsingException { |
| aclManager.addSystemUser(new SystemUser("sys-usr", new RepoPath("/home/users/system/foo"), new RepoPath("/home/users/system"))); |
| |
| aclManager.addUser(new User("test", new RepoPath("/home/users/test"), new RepoPath("/home/users/test"))); |
| aclManager.addAcl("sys-usr", newAcl(true, "jcr:read", "/home/users/notMatching")); |
| VaultPackageAssembler assembler = mock(VaultPackageAssembler.class); |
| when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); |
| Feature feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); |
| |
| FeaturesManager fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); |
| when(fm.getTargetFeature()).thenReturn(feature); |
| |
| aclManager.addRepoinitExtension(Collections.singletonList(assembler), fm); |
| |
| Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| assertNotNull(repoinitExtension); |
| |
| // in contrast to testUserHandlingWithMatchingUser in this test /home/users/test2 is not detected |
| // as user-home-path and thus is processed like a regular path (no 'home(uid)' repo-init statement and no exception).\ |
| // however, no attempt is made to create the path without any available node type information. |
| String expected = Util.normalize( |
| "create service user sys-usr with path system\n" + |
| "set ACL for sys-usr\n" + |
| " allow jcr:read on /home/users/notMatching\n" + |
| "end\n"); |
| |
| String actual = repoinitExtension.getText(); |
| assertEquals(expected, actual); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testCalculateEnforcedIntermediatePath() { |
| DefaultAclManager aclManager = new DefaultAclManager(null, "system"); |
| aclManager.calculateEnforcedIntermediatePath("/home/users/system/some/path"); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testAddRepoinitExtentionInvalidTxt() { |
| DefaultAclManager aclManager = new DefaultAclManager("/home/users/system/cq:services", "system"); |
| aclManager.addRepoinitExtention("some invalid txt", null, mock(FeaturesManager.class)); |
| } |
| |
| @Test |
| public void testAddRepoinitExtentionEmptyTxt() { |
| FeaturesManager fm = mock(FeaturesManager.class); |
| DefaultAclManager aclManager = new DefaultAclManager("/home/users/system/cq:services", "system"); |
| aclManager.addRepoinitExtention("", null, fm); |
| |
| verifyNoInteractions(fm); |
| } |
| |
| @Test |
| public void testAddRepoinitExtentionNullTxt() { |
| FeaturesManager fm = mock(FeaturesManager.class); |
| DefaultAclManager aclManager = new DefaultAclManager("/home/users/system/cq:services", "system"); |
| aclManager.addRepoinitExtention(null, null, fm); |
| |
| verifyNoInteractions(fm); |
| } |
| |
| private static AccessControlEntry newAcl(boolean isAllow, String privileges, String path) { |
| return new AccessControlEntry(isAllow, Arrays.asList(privileges.split(",")), new RepoPath(PlatformNameFormat.getRepositoryPath(path))); |
| } |
| |
| } |