This is an automated email from the ASF dual-hosted git repository. angela pushed a commit to branch SLING-9692 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git
commit b2a910911e125a4546ba9d1146f91bd042e0e548 Author: angela <[email protected]> AuthorDate: Fri Jan 15 08:05:49 2021 +0100 SLING-9692 : Add support for principal-based access control entries --- .../accesscontrol/AccessControlEntry.java | 13 ++ .../accesscontrol/DefaultAclManager.java | 15 ++ ...ntentPackage2FeatureModelConverterLauncher.java | 8 +- .../cpconverter/handlers/AbstractPolicyParser.java | 2 +- .../handlers/RepPrincipalPolicyEntryHandler.java | 121 +++++++++++++++ ...sling.feature.cpconverter.handlers.EntryHandler | 1 + .../accesscontrol/EnforcePrincipalBasedTest.java | 163 ++++++++++++++++++++ .../feature/cpconverter/handlers/ParseResult.java | 22 +++ .../handlers/RepPolicyEntryHandlerTest.java | 35 +---- .../RepPrincipalPolicyEntryHandlerTest.java | 164 +++++++++++++++++++++ .../services/random1/_rep_principalPolicy.xml | 26 ++++ .../services/random2/_rep_principalPolicy.xml | 29 ++++ .../services/random3/_rep_principalPolicy.xml | 26 ++++ .../services/random4/_rep_principalPolicy.xml | 26 ++++ 14 files changed, 616 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/AccessControlEntry.java b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/AccessControlEntry.java index 95151c4..984e7f3 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/AccessControlEntry.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/AccessControlEntry.java @@ -36,10 +36,17 @@ public final class AccessControlEntry { private final List<String> restrictions = new LinkedList<>(); + private final boolean isPrincipalBased; + public AccessControlEntry(boolean isAllow, @NotNull String privileges, @NotNull RepoPath repositoryPath) { + this(isAllow, privileges, repositoryPath, false); + } + + public AccessControlEntry(boolean isAllow, @NotNull String privileges, @NotNull RepoPath repositoryPath, boolean isPrincipalBased) { this.isAllow = isAllow; this.privileges = privileges; this.repositoryPath = repositoryPath; + this.isPrincipalBased = isPrincipalBased; } public void addRestriction(@Nullable String restriction) { @@ -64,6 +71,10 @@ public final class AccessControlEntry { return restrictions; } + public boolean isPrincipalBased() { + return isPrincipalBased; + } + @Override public String toString() { return "Acl [isAllow=" @@ -74,6 +85,8 @@ public final class AccessControlEntry { + repositoryPath + ", restrictions=" + restrictions + + ", isPrincipalBased=" + + isPrincipalBased + "]"; } diff --git a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java index 90c2277..76c5468 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/accesscontrol/DefaultAclManager.java @@ -51,6 +51,9 @@ public final class DefaultAclManager implements AclManager { private static final String CONTENT_XML_FILE_NAME = ".content.xml"; + private final boolean enforcePrincipalBased; + private RepoPath supportedPrincipalBasedPath; + private final Set<SystemUser> systemUsers = new LinkedHashSet<>(); private final Set<Group> groups = new LinkedHashSet<>(); private final Set<User> users = new LinkedHashSet<>(); @@ -61,6 +64,15 @@ public final class DefaultAclManager implements AclManager { private volatile PrivilegeDefinitions privilegeDefinitions; + public DefaultAclManager() { + this(false, null); + } + public DefaultAclManager(boolean enforcePrincipalBased, @Nullable String supportedPrincipalBasedPath) { + this.enforcePrincipalBased = enforcePrincipalBased; + this.supportedPrincipalBasedPath = (supportedPrincipalBasedPath == null) ? null : new RepoPath(supportedPrincipalBasedPath); + } + + @Override public boolean addUser(@NotNull User user) { return users.add(user); @@ -118,6 +130,7 @@ public final class DefaultAclManager implements AclManager { } } + // TODO: paths only should/need to be create with resource-based access control Set<RepoPath> paths = acls.entrySet().stream() .filter(entry -> getSystemUser(entry.getKey()).isPresent()) .map(Entry::getValue) @@ -136,6 +149,8 @@ public final class DefaultAclManager implements AclManager { path -> formatter.format("create path %s%n", path) ); + // TODO: generate 2 set of access control entries: principal-based und resource-based. + // TODO: if 'enforce-principal-based' is turned on all entries should be generated as prinicpal-based // add the acls acls.forEach((systemUserID, authorizations) -> getSystemUser(systemUserID).ifPresent(systemUser -> diff --git a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java index bc2d44b..8e778f6 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java @@ -96,6 +96,12 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna @Option(names = { "-Z", "--fail-on-mixed-packages" }, description = "Fail the conversion if the resulting attached content-package is MIXED type", required = false) private boolean failOnMixedPackages = false; + @Option(names = { "--enforce-principal-based" }, description = "Converts all service user access control entries to principal-based setup", required = false) + private boolean enforcePrincipalBased = false; + + @Option(names = { "--supported-principal-based-path" }, description = "Path supported for principal-based access control setup", required = false) + private String supportedPrincipalBasedPath = null; + @Override public void run() { if (quiet) { @@ -143,7 +149,7 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna .setFeaturesManager(featuresManager) .setBundlesDeployer(new DefaultArtifactsDeployer(artifactsOutputDirectory)) .setEntryHandlersManager(new DefaultEntryHandlersManager()) - .setAclManager(new DefaultAclManager()) + .setAclManager(new DefaultAclManager(enforcePrincipalBased, supportedPrincipalBasedPath)) .setEmitter(DefaultPackagesEventsEmitter.open(featureModelsOutputDirectory)) .setFailOnMixedPackages(failOnMixedPackages) .setDropContent(true); diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractPolicyParser.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractPolicyParser.java index fd68b31..31cd85e 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractPolicyParser.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/AbstractPolicyParser.java @@ -56,7 +56,7 @@ abstract class AbstractPolicyParser extends AbstractJcrNodeParser<Boolean> { this.aclManager = aclManager; } - private static @Nullable String extractValue(@Nullable String expression) { + static @Nullable String extractValue(@Nullable String expression) { if (expression == null || expression.isEmpty()) { return expression; } diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandler.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandler.java new file mode 100644 index 0000000..a3c276b --- /dev/null +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandler.java @@ -0,0 +1,121 @@ +/* + * 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.handlers; + +import org.apache.sling.feature.cpconverter.accesscontrol.AccessControlEntry; +import org.apache.sling.feature.cpconverter.accesscontrol.AclManager; +import org.apache.sling.feature.cpconverter.shared.RepoPath; +import org.jetbrains.annotations.NotNull; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +import javax.xml.transform.sax.TransformerHandler; +import java.util.Stack; + +import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE; + +public final class RepPrincipalPolicyEntryHandler extends AbstractPolicyEntryHandler { + + public RepPrincipalPolicyEntryHandler() { + super("/jcr_root(.*/)_rep_principalPolicy.xml"); + } + + @Override + @NotNull AbstractPolicyParser createPolicyParser(@NotNull RepoPath repositoryPath, @NotNull AclManager aclManager, @NotNull TransformerHandler handler) { + return new RepPrincipalPolicyParser(repositoryPath, + aclManager, + handler); + } + + private static final class RepPrincipalPolicyParser extends AbstractPolicyParser { + + private static final String REP_RESTRICTIONS = "rep:Restrictions"; + + private static final String REP_PRINCIPAL_NAME = "rep:principalName"; + + private static final String REP_PRIVILEGES = "rep:privileges"; + + private static final String REP_PRINCIPAL_POLICY = "rep:PrincipalPolicy"; + + private static final String REP_PRINCIPAL_ENTRY = "rep:PrincipalEntry"; + + private static final String REP_EFFECTIVE_PATH = "rep:effectivePath"; + + private final Stack<AccessControlEntry> aces = new Stack<>(); + + private boolean processCurrentAcl = false; + + private String principalName = null; + + public RepPrincipalPolicyParser(RepoPath repositoryPath, AclManager aclManager, TransformerHandler handler) { + super(REP_PRINCIPAL_POLICY, repositoryPath, aclManager, handler); + } + + @Override + protected void onJcrRootElement(String uri, String localName, String qName, Attributes attributes) { + super.onJcrRootElement(uri, localName, qName, attributes); + principalName = attributes.getValue(REP_PRINCIPAL_NAME); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + if (onRepAclNode) { + String primaryType = attributes.getValue(JCR_PRIMARYTYPE); + if (REP_PRINCIPAL_ENTRY.equals(primaryType)) { + if (principalName == null) { + throw new IllegalStateException("isolated principal-based access control entry. no principal found."); + } + String privileges = extractValue(attributes.getValue(REP_PRIVILEGES)); + RepoPath effectivePath = new RepoPath(attributes.getValue(REP_EFFECTIVE_PATH)); + + AccessControlEntry ace = new AccessControlEntry(true, privileges, effectivePath, true); + + processCurrentAcl = aclManager.addAcl(principalName, ace); + if (processCurrentAcl) { + aces.add(ace); + } else { + hasRejectedNodes = true; + } + } else if (REP_RESTRICTIONS.equals(primaryType) && !aces.isEmpty()) { + if (processCurrentAcl) { + AccessControlEntry ace = aces.peek(); + aces.add(ace); + addRestrictions(ace, attributes); + } + } + } else { + super.startElement(uri, localName, qName, attributes); + } + + if (!onRepAclNode || !processCurrentAcl) { + handler.startElement(uri, localName, qName, attributes); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (onRepAclNode && processCurrentAcl && !aces.isEmpty()) { + aces.pop(); + } else { + processCurrentAcl = false; + principalName = null; + handler.endElement(uri, localName, qName); + } + } + } +} diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler b/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler index 7a82e4e..46fb089 100644 --- a/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler +++ b/src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler @@ -7,6 +7,7 @@ org.apache.sling.feature.cpconverter.handlers.NodeTypesEntryHandler org.apache.sling.feature.cpconverter.handlers.PrivilegesHandler org.apache.sling.feature.cpconverter.handlers.PropertiesConfigurationEntryHandler org.apache.sling.feature.cpconverter.handlers.RepPolicyEntryHandler +org.apache.sling.feature.cpconverter.handlers.RepPrincipalPolicyEntryHandler org.apache.sling.feature.cpconverter.handlers.RepRepoPolicyEntryHandler org.apache.sling.feature.cpconverter.handlers.UsersEntryHandler org.apache.sling.feature.cpconverter.handlers.XmlConfigurationEntryHandler diff --git a/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/EnforcePrincipalBasedTest.java b/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/EnforcePrincipalBasedTest.java new file mode 100644 index 0000000..a7dc3fa --- /dev/null +++ b/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/EnforcePrincipalBasedTest.java @@ -0,0 +1,163 @@ +/* + * 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.sling.feature.ArtifactId; +import org.apache.sling.feature.Extension; +import org.apache.sling.feature.Feature; +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.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.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class EnforcePrincipalBasedTest { + + private final SystemUser systemUser = new SystemUser("user1", new RepoPath("/home/users/system/intermediate/usernode"), new RepoPath("/home/users/system/intermediate")); + + private AclManager aclManager; + private Path tempDir; + + private VaultPackageAssembler assembler; + private FeaturesManager fm; + private Feature feature; + + @Before + public void setUp() throws Exception { + aclManager = new DefaultAclManager(true, "/home/users/system/cq:services"); + tempDir = Files.createTempDirectory(getClass().getSimpleName()); + + assembler = mock(VaultPackageAssembler.class); + when(assembler.getEntry(anyString())).thenReturn(new File(System.getProperty("java.io.tmpdir"))); + feature = new Feature(new ArtifactId("org.apache.sling", "org.apache.sling.cp2fm", "0.0.1", null, null)); + + fm = Mockito.spy(new DefaultFeaturesManager(tempDir.toFile())); + when(fm.getTargetFeature()).thenReturn(feature); + } + + @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 testResourceBasedConversion() throws RepoInitParsingException { + aclManager.addSystemUser(systemUser); + + RepoPath accessControlledPath = new RepoPath("/content/feature"); + aclManager.addAcl("user1", new AccessControlEntry(true, "jcr:read", accessControlledPath , false)); + + aclManager.addRepoinitExtension(Arrays.asList(assembler), fm); + + Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); + assertNotNull(repoinitExtension); + + String expected = + "create service user user1 with path /home/users/system/cq:services/intermediate" + System.lineSeparator() + + "set principal ACL for user1" + System.lineSeparator() + + "allow jcr:read on /content/feature" + System.lineSeparator() + + "end" + System.lineSeparator(); + + 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 testPrincipalBased() throws RepoInitParsingException { + aclManager.addSystemUser(systemUser); + + RepoPath accessControlledPath = new RepoPath("/content/feature"); + aclManager.addAcl("user1", new AccessControlEntry(true, "jcr:read", accessControlledPath, true)); + + aclManager.addRepoinitExtension(Arrays.asList(assembler), fm); + + Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); + assertNotNull(repoinitExtension); + + String expected = + "create service user user1 with path /home/users/system/cq:services/intermediate" + System.lineSeparator() + + "set principal ACL for user1" + System.lineSeparator() + + "allow jcr:read on /content/feature" + System.lineSeparator() + + "end" + System.lineSeparator(); + + 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 testPrincipalBasedForUserHome() throws RepoInitParsingException { + aclManager.addSystemUser(systemUser); + + RepoPath accessControlledPath = new RepoPath("/home/users/system/cq:services/intermediate/usernode"); + AccessControlEntry acl = new AccessControlEntry(true, "jcr:read", accessControlledPath, true); + aclManager.addAcl("user1", acl); + + aclManager.addRepoinitExtension(Arrays.asList(assembler), fm); + + Extension repoinitExtension = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); + assertNotNull(repoinitExtension); + + String expected = + "create service user user1 with path /home/users/system/cq:services/intermediate" + System.lineSeparator() + + "set principal ACL for user1" + System.lineSeparator() + + "allow jcr:read on home(user1)" + System.lineSeparator() + + "end" + System.lineSeparator(); + + String actual = repoinitExtension.getText(); + assertEquals(expected, actual); + + RepoInitParser repoInitParser = new RepoInitParserService(); + List<Operation> operations = repoInitParser.parse(new StringReader(actual)); + assertFalse(operations.isEmpty()); + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/ParseResult.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ParseResult.java new file mode 100644 index 0000000..3952f3a --- /dev/null +++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ParseResult.java @@ -0,0 +1,22 @@ +package org.apache.sling.feature.cpconverter.handlers; + +import org.apache.sling.feature.Extension; + +final class ParseResult { + + private final Extension repoinitExtension; + private final String excludedAcls; + + public ParseResult(Extension repoinitExtension, String excludedAcls) { + this.repoinitExtension = repoinitExtension; + this.excludedAcls = excludedAcls; + } + + public Extension getRepoinitExtension() { + return repoinitExtension; + } + + public String getExcludedAcls() { + return excludedAcls; + } +} diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java index e2d9889..d72adb6 100644 --- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java +++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPolicyEntryHandlerTest.java @@ -16,22 +16,14 @@ */ package org.apache.sling.feature.cpconverter.handlers; -import org.apache.jackrabbit.vault.fs.io.Archive; -import org.apache.jackrabbit.vault.fs.io.Archive.Entry; -import org.apache.sling.feature.ArtifactId; import org.apache.sling.feature.Extension; import org.apache.sling.feature.ExtensionType; -import org.apache.sling.feature.Feature; -import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter; import org.apache.sling.feature.cpconverter.accesscontrol.AclManager; import org.apache.sling.feature.cpconverter.accesscontrol.DefaultAclManager; import org.apache.sling.feature.cpconverter.accesscontrol.Group; import org.apache.sling.feature.cpconverter.accesscontrol.SystemUser; import org.apache.sling.feature.cpconverter.accesscontrol.User; -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.impl.RepoInitParserService; import org.apache.sling.repoinit.parser.operations.Operation; @@ -41,9 +33,7 @@ import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.StringReader; -import java.util.Arrays; import java.util.List; import static org.junit.Assert.assertEquals; @@ -51,10 +41,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; public final class RepPolicyEntryHandlerTest { @@ -244,7 +232,7 @@ public final class RepPolicyEntryHandlerTest { "allow jcr:read,rep:userManagement on /home/groups/g" + System.lineSeparator() + "end" + System.lineSeparator(); assertEquals(expected, repoinitExtension.getText()); - assertTrue(result.excludedAcls.isEmpty()); + assertTrue(result.getExcludedAcls().isEmpty()); } @Test @@ -272,7 +260,7 @@ public final class RepPolicyEntryHandlerTest { String expectedExclusions = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><jcr:root xmlns:jcr=\"http://www.jcp.org/jcr/1.0\" xmlns:rep=\"internal\" jcr:primaryType=\"rep:ACL\">\n" + " <allow1 jcr:primaryType=\"rep:GrantACE\" rep:principalName=\"testgroup\" rep:privileges=\"{Name}[jcr:read]\"/>\n" + "</jcr:root>\n"; - assertEquals(expectedExclusions, result.excludedAcls); + assertEquals(expectedExclusions, result.getExcludedAcls()); } @Test(expected = IllegalStateException.class) @@ -326,23 +314,4 @@ public final class RepPolicyEntryHandlerTest { ByteArrayOutputStream baos = new ByteArrayOutputStream(); return new ParseResult(TestUtils.createRepoInitExtension(handler, aclManager, path, getClass().getResourceAsStream(path.substring(1)), baos), new String(baos.toByteArray())); } - - private static final class ParseResult { - - private final Extension repoinitExtension; - private final String excludedAcls; - - public ParseResult(Extension repoinitExtension, String excludedAcls) { - this.repoinitExtension = repoinitExtension; - this.excludedAcls = excludedAcls; - } - - public Extension getRepoinitExtension() { - return repoinitExtension; - } - - public String getExcludedAcls() { - return excludedAcls; - } - } } diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandlerTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandlerTest.java new file mode 100644 index 0000000..6fba859 --- /dev/null +++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandlerTest.java @@ -0,0 +1,164 @@ +/* + * 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.handlers; + +import org.apache.sling.feature.Extension; +import org.apache.sling.feature.ExtensionType; +import org.apache.sling.feature.cpconverter.accesscontrol.AclManager; +import org.apache.sling.feature.cpconverter.accesscontrol.DefaultAclManager; +import org.apache.sling.feature.cpconverter.accesscontrol.SystemUser; +import org.apache.sling.feature.cpconverter.shared.RepoPath; +import org.apache.sling.repoinit.parser.RepoInitParser; +import org.apache.sling.repoinit.parser.impl.RepoInitParserService; +import org.apache.sling.repoinit.parser.operations.Operation; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.StringReader; +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.assertTrue; + +public final class RepPrincipalPolicyEntryHandlerTest { + + private RepPrincipalPolicyEntryHandler handler; + + @Before + public void setUp() { + handler = new RepPrincipalPolicyEntryHandler(); + } + + @After + public void tearDown() { + handler = null; + } + + @Test + public void doesNotMatch() { + assertFalse(handler.matches("/this/is/a/path/not/pointing/to/a/valid/_rep_principalPolicy.xml")); + assertFalse(handler.matches("/home/users/system/asd-share-commons/asd-index-definition-reader/_rep_principalPolicy.xml")); + assertFalse(handler.matches("/jcr_root/home/users/system/services/feature/random1/_rep_policy.xml")); + } + + @Test + public void matches() { + assertTrue(handler.matches("/jcr_root/home/users/system/services/random1/_rep_principalPolicy.xml")); + } + + @Test + public void parseSimplePolicy() throws Exception { + Extension repoinitExtension = parseAndSetRepoinit("service1", "random1").getRepoinitExtension(); + assertNotNull(repoinitExtension); + assertEquals(ExtensionType.TEXT, repoinitExtension.getType()); + + String expected = + "create service user service1 with path /home/users/system/services" + System.lineSeparator() + + "set principal ACL for service1\n" + + "allow jcr:read,jcr:readAccessControl on /asd/public\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 parseMvRestrictions() throws Exception { + Extension repoinitExtension = parseAndSetRepoinit("service2", "random2").getRepoinitExtension(); + assertNotNull(repoinitExtension); + assertEquals(ExtensionType.TEXT, repoinitExtension.getType()); + + String expected = + "create service user service2 with path /home/users/system/services" + System.lineSeparator() + + "set principal ACL for service2\n" + + "allow jcr:read on /asd/public restriction(rep:ntNames,nt:folder,sling:Folder)\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 parseUserHome() throws Exception { + Extension repoinitExtension = parseAndSetRepoinit("service3", "random3").getRepoinitExtension(); + assertNotNull(repoinitExtension); + assertEquals(ExtensionType.TEXT, repoinitExtension.getType()); + + String expected = + "create service user service3 with path /home/users/system/services" + System.lineSeparator() + + "set principal ACL for service3\n" + + "allow jcr:all on home(service3)/subtree\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 parseOtherUserHomeSubtree() throws Exception { + Extension repoinitExtension = parseAndSetRepoinit("service4", "random4").getRepoinitExtension(); + assertNotNull(repoinitExtension); + assertEquals(ExtensionType.TEXT, repoinitExtension.getType()); + + String expected = + "create service user service4 with path /home/users/system/services" + System.lineSeparator() + + "set principal ACL for service4\n" + + "allow jcr:read,rep:userManagement on home(service3)\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()); + } + + private ParseResult parseAndSetRepoinit(@NotNull String systemUsersName, @NotNull String nodeName) throws Exception { + RepoPath repoPath = new RepoPath("/home/users/system/services/"+nodeName); + return parseAndSetRepoinit(new SystemUser(systemUsersName, repoPath, new RepoPath("/home/users/system/services"))); + } + + private ParseResult parseAndSetRepoinit(SystemUser systemUser) throws Exception { + String path = "/jcr_root"+systemUser.getPath().toString() + "/_rep_principalPolicy.xml"; + AclManager aclManager = new DefaultAclManager(); + aclManager.addSystemUser(systemUser); + + InputStream is = getClass().getResourceAsStream(path.substring(1)); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + return new ParseResult(TestUtils.createRepoInitExtension(handler, aclManager, path, is, baos), new String(baos.toByteArray())); + } + +} diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random1/_rep_principalPolicy.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random1/_rep_principalPolicy.xml new file mode 100644 index 0000000..e55c57f --- /dev/null +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random1/_rep_principalPolicy.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" + jcr:primaryType="rep:PrincipalPolicy" + rep:principalName="service1"> + <entry0 + jcr:primaryType="rep:PrincipalEntry" + rep:privileges="{Name}[jcr:read,jcr:readAccessControl]" + rep:effectivePath="/asd/public"> + </entry0> +</jcr:root> diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random2/_rep_principalPolicy.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random2/_rep_principalPolicy.xml new file mode 100644 index 0000000..38f5335 --- /dev/null +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random2/_rep_principalPolicy.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" + jcr:primaryType="rep:PrincipalPolicy" + rep:principalName="service2"> + <entry0 + jcr:primaryType="rep:PrincipalEntry" + rep:privileges="{Name}[jcr:read]" + rep:effectivePath="/asd/public"> + <rep:restrictions + jcr:primaryType="rep:Restrictions" + rep:ntNames="{Name}[nt:folder,sling:Folder]"/> + </entry0> +</jcr:root> diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random3/_rep_principalPolicy.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random3/_rep_principalPolicy.xml new file mode 100644 index 0000000..f5048c0 --- /dev/null +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random3/_rep_principalPolicy.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" + jcr:primaryType="rep:PrincipalPolicy" + rep:principalName="service3"> + <entry0 + jcr:primaryType="rep:PrincipalEntry" + rep:privileges="{Name}[jcr:all]" + rep:effectivePath="/home/users/system/services/random3/subtree"> + </entry0> +</jcr:root> diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random4/_rep_principalPolicy.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random4/_rep_principalPolicy.xml new file mode 100644 index 0000000..eff8224 --- /dev/null +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/jcr_root/home/users/system/services/random4/_rep_principalPolicy.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" + jcr:primaryType="rep:PrincipalPolicy" + rep:principalName="service4"> + <entry0 + jcr:primaryType="rep:PrincipalEntry" + rep:privileges="{Name}[jcr:read,rep:userManagement]" + rep:effectivePath="/home/users/system/services/random3"> + </entry0> +</jcr:root>
