This is an automated email from the ASF dual-hosted git repository.
pauls pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git
The following commit(s) were added to refs/heads/master by this push:
new 8a04987 SLING-9692: Add support for principal-based access control
entries (#52)
8a04987 is described below
commit 8a04987a6f539525728a1927232352696d3c7c58
Author: Karl Pauls <[email protected]>
AuthorDate: Fri Jan 15 16:17:28 2021 +0100
SLING-9692: Add support for principal-based access control entries (#52)
* SLING-9692 : Add support for principal-based access control entries
Co-authored-by: angela <[email protected]>
---
.../accesscontrol/AccessControlEntry.java | 13 ++
.../accesscontrol/DefaultAclManager.java | 155 +++++++++-----
...ntentPackage2FeatureModelConverterLauncher.java | 8 +-
.../cpconverter/handlers/AbstractPolicyParser.java | 2 +-
.../handlers/RepPrincipalPolicyEntryHandler.java | 119 +++++++++++
...sling.feature.cpconverter.handlers.EntryHandler | 1 +
.../accesscontrol/EnforcePrincipalBasedTest.java | 234 +++++++++++++++++++++
.../handlers/GroupEntryHandlerTest.java | 73 +++++++
.../feature/cpconverter/handlers/ParseResult.java | 48 +++++
.../handlers/RepPolicyEntryHandlerTest.java | 54 +----
.../RepPrincipalPolicyEntryHandlerTest.java | 177 ++++++++++++++++
.../handlers/RepRepoPolicyEntryHandlerTest.java | 9 +-
.../handlers/UsersEntryHandlerTest.java | 18 ++
.../services/random1/_rep_principalPolicy.xml | 26 +++
.../services/random2/_rep_principalPolicy.xml | 29 +++
.../services/random3/_rep_principalPolicy.xml | 26 +++
.../services/random4/_rep_principalPolicy.xml | 26 +++
17 files changed, 905 insertions(+), 113 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..728343b 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
@@ -47,10 +47,13 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-public final class DefaultAclManager implements AclManager {
+public class DefaultAclManager implements AclManager {
private static final String CONTENT_XML_FILE_NAME = ".content.xml";
+ private final boolean enforcePrincipalBased;
+ private final 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);
@@ -97,7 +109,7 @@ public final class DefaultAclManager implements AclManager {
for (SystemUser systemUser : systemUsers) {
// make sure all users are created first
- formatter.format("create service user %s with path %s%n",
systemUser.getId(), systemUser.getIntermediatePath());
+ formatter.format("create service user %s with path %s%n",
systemUser.getId(),
calculateIntermediatePath(systemUser.getIntermediatePath()));
if (aclIsBelow(systemUser.getPath())) {
throw new IllegalStateException("Detected policy on
subpath of system-user: " + systemUser);
}
@@ -118,29 +130,33 @@ public final class DefaultAclManager implements
AclManager {
}
}
- Set<RepoPath> paths = acls.entrySet().stream()
- .filter(entry -> getSystemUser(entry.getKey()).isPresent())
- .map(Entry::getValue)
- .flatMap(Collection::stream)
- .map(AccessControlEntry::getRepositoryPath)
- .collect(Collectors.toSet());
-
- paths.stream()
- .filter(path -> !paths.stream().anyMatch(other ->
!other.equals(path) && other.startsWith(path)))
-
.filter(((Predicate<RepoPath>)RepoPath::isRepositoryPath).negate())
- .filter(path -> Stream.of(systemUsers, users,
groups).flatMap(Collection::stream)
- .noneMatch(user ->
user.getPath().startsWith(path)))
- .map(path -> computePathWithTypes(path, packageAssemblers))
- .filter(Objects::nonNull)
- .forEach(
- path -> formatter.format("create path %s%n", path)
- );
+ if (!enforcePrincipalBased) {
+ Set<RepoPath> paths = acls.entrySet().stream()
+ .filter(entry ->
getSystemUser(entry.getKey()).isPresent())
+ .map(Entry::getValue)
+ .flatMap(Collection::stream)
+ // paths only should/need to be create with
resource-based access control
+ .filter(((Predicate<AccessControlEntry>)
AccessControlEntry::isPrincipalBased).negate())
+ .map(AccessControlEntry::getRepositoryPath)
+ .collect(Collectors.toSet());
+
+ paths.stream()
+ .filter(path -> paths.stream().noneMatch(other ->
!other.equals(path) && other.startsWith(path)))
+
.filter(((Predicate<RepoPath>)RepoPath::isRepositoryPath).negate())
+ .filter(path -> Stream.of(systemUsers, users,
groups).flatMap(Collection::stream)
+ .noneMatch(user ->
user.getPath().startsWith(path)))
+ .map(path -> computePathWithTypes(path,
packageAssemblers))
+ .filter(Objects::nonNull)
+ .forEach(
+ path -> formatter.format("create path %s%n",
path)
+ );
+ }
// add the acls
acls.forEach((systemUserID, authorizations) ->
- getSystemUser(systemUserID).ifPresent(systemUser ->
- addStatements(systemUser, authorizations,
packageAssemblers, formatter)
- ));
+ getSystemUser(systemUserID).ifPresent(systemUser ->
+ addStatements(systemUser, authorizations,
formatter)
+ ));
String text = formatter.toString();
@@ -150,48 +166,76 @@ public final class DefaultAclManager implements
AclManager {
}
}
- private boolean aclStartsWith(RepoPath path) {
+ @NotNull
+ private String calculateIntermediatePath(@NotNull RepoPath
intermediatePath) {
+ if (enforcePrincipalBased && supportedPrincipalBasedPath != null &&
!intermediatePath.startsWith(supportedPrincipalBasedPath)) {
+ RepoPath parent = intermediatePath.getParent();
+ while (parent != null) {
+ if (supportedPrincipalBasedPath.startsWith(parent)) {
+ String relpath =
intermediatePath.toString().substring(parent.toString().length());
+ return supportedPrincipalBasedPath.toString() + relpath;
+ }
+ parent = parent.getParent();
+ }
+ throw new IllegalStateException("Cannot calculate intermediate
path for service user. Configured Supported path "
+supportedPrincipalBasedPath+" has no common ancestor with "+intermediatePath);
+ } else {
+ return intermediatePath.toString();
+ }
+ }
+
+ private boolean aclStartsWith(@NotNull RepoPath path) {
return acls.values().stream().flatMap(List::stream).anyMatch(acl ->
acl.getRepositoryPath().startsWith(path));
}
- private boolean aclIsBelow(RepoPath path) {
+ private boolean aclIsBelow(@NotNull RepoPath path) {
return acls.values().stream().flatMap(List::stream).anyMatch(acl ->
acl.getRepositoryPath().startsWith(path) &&
!acl.getRepositoryPath().equals(path));
}
private void addStatements(@NotNull SystemUser systemUser,
@NotNull List<AccessControlEntry>
authorizations,
- @NotNull List<VaultPackageAssembler>
packageAssemblers,
@NotNull Formatter formatter) {
if (authorizations.isEmpty()) {
return;
}
- Map<AccessControlEntry, String> entries = new LinkedHashMap<>();
+ Map<AccessControlEntry, String> resourceEntries = new
LinkedHashMap<>();
+ Map<AccessControlEntry, String> principalEntries = new
LinkedHashMap<>();
+
authorizations.forEach(entry -> {
String path = getRepoInitPath(entry.getRepositoryPath(),
systemUser);
- if (path != null) {
- entries.put(entry, path);
+ if (enforcePrincipalBased || entry.isPrincipalBased()) {
+ principalEntries.put(entry, path);
+ } else {
+ resourceEntries.put(entry, path);
}
});
- if (!entries.isEmpty()) {
- formatter.format("set ACL for %s%n", systemUser.getId());
- entries.forEach((entry, path) -> {
- formatter.format("%s %s on %s",
- entry.getOperation(),
- entry.getPrivileges(),
- path);
-
- if (!entry.getRestrictions().isEmpty()) {
- formatter.format(" restriction(%s)",
- String.join(",", entry.getRestrictions()));
- }
- formatter.format("%n");
- });
+ if (!principalEntries.isEmpty()) {
+ formatter.format("set principal ACL for %s%n", systemUser.getId());
+ principalEntries.forEach((entry, path) -> writeEntry(entry, path,
formatter));
+ formatter.format("end%n");
+ }
+ if (!resourceEntries.isEmpty()) {
+ formatter.format("set ACL for %s%n", systemUser.getId());
+ resourceEntries.forEach((entry, path) -> writeEntry(entry, path,
formatter));
formatter.format("end%n");
}
}
+ private void writeEntry(@NotNull AccessControlEntry entry, @NotNull String
path, @NotNull Formatter formatter) {
+ formatter.format("%s %s on %s",
+ entry.getOperation(),
+ entry.getPrivileges(),
+ path);
+
+ if (!entry.getRestrictions().isEmpty()) {
+ formatter.format(" restriction(%s)",
+ String.join(",", entry.getRestrictions()));
+ }
+
+ formatter.format("%n");
+ }
+
private @NotNull Optional<SystemUser> getSystemUser(@NotNull String id) {
return systemUsers.stream().filter(systemUser ->
systemUser.getId().equals(id)).findFirst();
}
@@ -215,7 +259,7 @@ public final class DefaultAclManager implements AclManager {
privilegeDefinitions = null;
}
- private static @Nullable String computePathWithTypes(@NotNull RepoPath
path, @NotNull List<VaultPackageAssembler> packageAssemblers) {
+ protected @Nullable String computePathWithTypes(@NotNull RepoPath path,
@NotNull List<VaultPackageAssembler> packageAssemblers) {
path = new
RepoPath(PlatformNameFormat.getPlatformPath(path.toString()));
boolean type = false;
@@ -228,7 +272,7 @@ public final class DefaultAclManager implements AclManager {
String primary;
String mixin;
try (FileInputStream input = new
FileInputStream(currentContent);
- FileInputStream input2 = new
FileInputStream(currentContent)) {
+ FileInputStream input2 = new
FileInputStream(currentContent)) {
primary = new PrimaryTypeParser().parse(input);
mixin = new MixinParser().parse(input2);
current += "(" + primary;
@@ -254,24 +298,25 @@ public final class DefaultAclManager implements
AclManager {
return type ? new RepoPath(current).toString() : null;
}
- @Nullable
+ @NotNull
private String getRepoInitPath(@NotNull RepoPath path, @NotNull SystemUser
systemUser) {
if (path.isRepositoryPath()) {
return ":repository";
} else if (isHomePath(path, systemUser.getPath())) {
- return getHomePath(path, systemUser);
+ return getHomePath(systemUser);
} else {
AbstractUser other = getOtherUser(path, Stream.of(systemUsers,
groups).flatMap(Collection::stream));
if (other != null) {
- return getHomePath(path, other);
+ return getHomePath(other);
}
// not a special path
return path.toString();
}
}
- private static boolean isHomePath(@NotNull RepoPath path, @NotNull
RepoPath systemUserPath) {
- return path.startsWith(systemUserPath);
+ private boolean isHomePath(@NotNull RepoPath path, @NotNull RepoPath
systemUserPath) {
+ // ACE located in the subtree are not supported
+ return path.equals(systemUserPath);
}
@Nullable
@@ -280,14 +325,10 @@ public final class DefaultAclManager implements
AclManager {
}
@NotNull
- private static String getHomePath(@NotNull RepoPath path, @NotNull
AbstractUser abstractUser) {
- return getHomePath(path, abstractUser.getPath(), abstractUser.getId());
- }
-
- @NotNull
- private static String getHomePath(@NotNull RepoPath path, @NotNull
RepoPath userPath, @NotNull String id) {
- String subpath = (path.equals(userPath) ? "" :
path.toString().substring(userPath.toString().length()));
- return "home("+id+")"+subpath;
+ private String getHomePath(@NotNull AbstractUser abstractUser) {
+ // since ACEs located in the subtree of a user are not supported by
the converter,
+ // there is no need to calculate a potential sub-path to be appended.
+ return "home("+abstractUser.getId()+")";
}
private static void registerPrivileges(@NotNull PrivilegeDefinitions
definitions, @NotNull Formatter formatter) {
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..1872c56
--- /dev/null
+++
b/src/main/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandler.java
@@ -0,0 +1,119 @@
+/*
+ * 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() && 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..51d9884
--- /dev/null
+++
b/src/test/java/org/apache/sling/feature/cpconverter/accesscontrol/EnforcePrincipalBasedTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+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/some/subtree");
+ 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(expected = IllegalStateException.class)
+ public void testInvalidSupportedPath() {
+ AclManager aclManager = new DefaultAclManager(true,
"/an/invalid/supported/path");
+ aclManager.addSystemUser(systemUser);
+
+ RepoPath accessControlledPath = new RepoPath("/content/feature");
+ aclManager.addAcl(systemUser.getId(), new AccessControlEntry(true,
"jcr:read", accessControlledPath , false));
+
+ aclManager.addRepoinitExtension(Arrays.asList(assembler), fm);
+
+ }
+
+ @Test
+ public void testResourceBasedConversionWithoutForce() throws
RepoInitParsingException {
+ AclManager aclManager = new DefaultAclManager(false,
"/home/users/system/some/subtree"){
+ @Override
+ protected @Nullable String computePathWithTypes(@NotNull RepoPath
path, @NotNull List<VaultPackageAssembler> packageAssemblers) {
+ return "/content/feature(sling:Folder)";
+ }
+ };
+ aclManager.addSystemUser(systemUser);
+
+ RepoPath accessControlledPath = new RepoPath("/content/feature");
+ aclManager.addAcl(systemUser.getId(), 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/intermediate" + System.lineSeparator() +
+ "create path /content/feature(sling:Folder)" +
System.lineSeparator() +
+ "set 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());
+
+ aclManager =
EnforcePrincipalBasedTest.this.aclManager;aclManager.addSystemUser(systemUser);
+ feature.getExtensions().clear();
+
+ accessControlledPath = new RepoPath("/content/feature");
+ aclManager.addAcl(systemUser.getId(), new AccessControlEntry(true,
"jcr:read", accessControlledPath , false));
+
+ aclManager.addRepoinitExtension(Arrays.asList(assembler), fm);
+
+ repoinitExtension =
feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT);
+ assertNotNull(repoinitExtension);
+
+ expected =
+ "create service user user1 with path
/home/users/system/some/subtree/intermediate" + System.lineSeparator() +
+ "set principal ACL for user1" + System.lineSeparator()
+
+ "allow jcr:read on /content/feature" +
System.lineSeparator() +
+ "end" + System.lineSeparator();
+
+ actual = repoinitExtension.getText();
+ assertEquals(expected, actual);
+
+ repoInitParser = new RepoInitParserService();
+ operations = repoInitParser.parse(new StringReader(actual));
+ assertFalse(operations.isEmpty());
+ }
+
+ @Test
+ public void testResourceBasedConversion() throws RepoInitParsingException {
+ aclManager.addSystemUser(systemUser);
+
+ RepoPath accessControlledPath = new RepoPath("/content/feature");
+ aclManager.addAcl(systemUser.getId(), 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/some/subtree/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/some/subtree/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 = systemUser.getPath();
+ 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/some/subtree/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());
+ }
+}
diff --git
a/src/test/java/org/apache/sling/feature/cpconverter/handlers/GroupEntryHandlerTest.java
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/GroupEntryHandlerTest.java
new file mode 100644
index 0000000..97f2650
--- /dev/null
+++
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/GroupEntryHandlerTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.AclManager;
+import org.apache.sling.feature.cpconverter.accesscontrol.Group;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class GroupEntryHandlerTest {
+
+ private GroupEntryHandler handler;
+
+ @Before
+ public void setUp() {
+ handler = new GroupEntryHandler();
+ }
+
+ @After
+ public void tearDown() {
+ handler = null;
+ }
+
+ @Test
+ public void doesNotMatch() {
+
assertFalse(handler.matches("/this/is/a/path/not/pointing/to/a/valid/grouop.asd"));
+ assertFalse(handler.matches("/home/groups/g/groupnode/.content.xml"));
+ }
+
+ @Test
+ public void matches() {
+
assertTrue(handler.matches("/jcr_root/home/groups/g/groupnode/.content.xml"));
+ }
+
+ @Test
+ public void parseGroup() throws Exception {
+ String path =
"/jcr_root/home/groups/g/V084LLw1ypl2l9G0e28c/.content.xml";
+ AclManager aclManager = mock(AclManager.class);
+ TestUtils.createRepoInitExtension(handler, aclManager, path,
getClass().getResourceAsStream(path.substring(1)));
+ verify(aclManager, times(1)).addGroup(any(Group.class));
+ }
+
+ @Test
+ public void parseUser() throws Exception {
+ String path = "/jcr_root/home/users/a/author/.content.xml";
+ AclManager aclManager = mock(AclManager.class);
+ TestUtils.createRepoInitExtension(handler, aclManager, path,
getClass().getResourceAsStream(path.substring(1)));
+ verify(aclManager, never()).addGroup(any(Group.class));
+ }
+}
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..7f82909
--- /dev/null
+++
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/ParseResult.java
@@ -0,0 +1,48 @@
+/*
+ * 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.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+final class ParseResult {
+
+ private final Extension repoinitExtension;
+ private final String excludedAcls;
+
+ ParseResult(@Nullable Extension repoinitExtension, @NotNull String
excludedAcls) {
+ assertNotNull(repoinitExtension);
+ assertEquals(ExtensionType.TEXT, repoinitExtension.getType());
+ this.repoinitExtension = repoinitExtension;
+ this.excludedAcls = excludedAcls;
+ }
+
+ @NotNull
+ Extension getRepoinitExtension() {
+ return repoinitExtension;
+ }
+
+ @NotNull
+ 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..e1c4b48 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 {
@@ -94,8 +82,6 @@ public final class RepPolicyEntryHandlerTest {
"acs-commons-ensure-service-user-service",
"acs-commons-automatic-package-replicator-service",
"acs-commons-on-deploy-scripts-service").getRepoinitExtension();
- assertNotNull(repoinitExtension);
- assertEquals(ExtensionType.TEXT, repoinitExtension.getType());
// commented ACLs are due SLING-8561
String expected =
@@ -142,9 +128,6 @@ public final class RepPolicyEntryHandlerTest {
"acs-commons-on-deploy-scripts-service");
Extension repoinitExtension = result.getRepoinitExtension();
- assertNotNull(repoinitExtension);
- assertEquals(ExtensionType.TEXT, repoinitExtension.getType());
-
String expected =
"create service user
acs-commons-package-replication-status-event-service with path
/home/users/system" + System.lineSeparator() +
"create service user acs-commons-ensure-service-user-service
with path /home/users/system" + System.lineSeparator() +
@@ -188,9 +171,6 @@ public final class RepPolicyEntryHandlerTest {
ParseResult result = parseAndSetRepoinit(new
SystemUser("acs-commons-package-replication-status-event-service",
new RepoPath("/this/is/a/completely/different/path/foo"), new
RepoPath("/this/is/a/completely/different/path")));
Extension repoinitExtension = result.getRepoinitExtension();
- assertNotNull(repoinitExtension);
- assertEquals(ExtensionType.TEXT, repoinitExtension.getType());
-
String expected =
"create service user
acs-commons-package-replication-status-event-service with path
/this/is/a/completely/different/path" + System.lineSeparator() +
"set ACL for
acs-commons-package-replication-status-event-service" + System.lineSeparator() +
@@ -222,8 +202,8 @@ public final class RepPolicyEntryHandlerTest {
@Test
public void parseEmptyAcl() throws Exception {
- Extension repoinitExtension = parseAndSetRepoinit(new String[]
{}).getRepoinitExtension();
- assertNull(repoinitExtension);
+ Extension extension = TestUtils.createRepoInitExtension(handler, new
DefaultAclManager(), "/jcr_root/home/users/system/asd/_rep_policy.xml",
getClass().getResourceAsStream("/jcr_root/home/users/system/asd/_rep_policy.xml".substring(1)),
new ByteArrayOutputStream());
+ assertNull(extension);
}
@Test
@@ -235,8 +215,6 @@ public final class RepPolicyEntryHandlerTest {
ParseResult result =
parseAndSetRepoInit("/jcr_root/home/groups/g/_rep_policy.xml", aclManager);
Extension repoinitExtension = result.getRepoinitExtension();
- assertNotNull(repoinitExtension);
- assertEquals(ExtensionType.TEXT, repoinitExtension.getType());
String expected =
"create service user service1 with path
/home/users/system/services" + System.lineSeparator() +
@@ -244,7 +222,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
@@ -258,8 +236,6 @@ public final class RepPolicyEntryHandlerTest {
ParseResult result =
parseAndSetRepoInit("/jcr_root/home/groups/g/HjDnfdMCjekaF4jhhUvO/_rep_policy.xml",
aclManager);
Extension repoinitExtension = result.getRepoinitExtension();
- assertNotNull(repoinitExtension);
- assertEquals(ExtensionType.TEXT, repoinitExtension.getType());
String expected =
"create service user service1 with path
/home/users/system/services" + System.lineSeparator() +
@@ -272,7 +248,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)
@@ -317,8 +293,7 @@ public final class RepPolicyEntryHandlerTest {
for (SystemUser systemUser : systemUsers) {
aclManager.addSystemUser(systemUser);
}
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- return new ParseResult(TestUtils.createRepoInitExtension(handler,
aclManager, path, getClass().getResourceAsStream(path.substring(1)), baos), new
String(baos.toByteArray()));
+ return parseAndSetRepoInit(path, aclManager);
}
@NotNull
@@ -326,23 +301,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..9849b4f
--- /dev/null
+++
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepPrincipalPolicyEntryHandlerTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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();
+ 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();
+ 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(expected = IllegalStateException.class)
+ public void parsePolicyInSubtree() throws Exception {
+ parseAndSetRepoinit("service3", "random3").getRepoinitExtension();
+ }
+
+ @Test
+ public void parseOtherUserHomeMissing() throws Exception {
+ SystemUser systemUser4 = createSystemUser("service4", "random4");
+
+ Extension repoinitExtension =
parseAndSetRepoinit(getPolicyPath(systemUser4),
systemUser4).getRepoinitExtension();
+ String expected =
+ "create service user service4 with path
/home/users/system/services" + System.lineSeparator() +
+ "set principal ACL for service4" + System.lineSeparator() +
+ // since service3 is not known to the AclManager it treats the
effective path as a regular node.
+ "allow jcr:read,rep:userManagement on
/home/users/system/services/random3" + 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 parseOtherUserHome() throws Exception {
+ SystemUser systemUser3 = createSystemUser("service3", "random3");
+ SystemUser systemUser4 = createSystemUser("service4", "random4");
+
+ Extension repoinitExtension =
parseAndSetRepoinit(getPolicyPath(systemUser4), systemUser4,
systemUser3).getRepoinitExtension();
+ String expected =
+ "create service user service4 with path
/home/users/system/services" + System.lineSeparator() +
+ "create service user service3 with path
/home/users/system/services" + System.lineSeparator() +
+ "set principal ACL for service4" + System.lineSeparator() +
+ "allow jcr:read,rep:userManagement on home(service3)" +
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());
+ }
+
+ @NotNull
+ private static SystemUser createSystemUser(@NotNull String
systemUsersName, @NotNull String nodeName) {
+ RepoPath repoPath = new
RepoPath("/home/users/system/services/"+nodeName);
+ return new SystemUser(systemUsersName, repoPath, new
RepoPath("/home/users/system/services"));
+ }
+
+ @NotNull
+ private static String getPolicyPath(@NotNull SystemUser systemUser) {
+ return "/jcr_root"+systemUser.getPath().toString() +
"/_rep_principalPolicy.xml";
+ }
+
+ private ParseResult parseAndSetRepoinit(@NotNull String systemUsersName,
@NotNull String nodeName) throws Exception {
+ SystemUser systemUser = createSystemUser(systemUsersName, nodeName);
+
+ return parseAndSetRepoinit(getPolicyPath(systemUser), systemUser);
+ }
+
+ private ParseResult parseAndSetRepoinit(@NotNull String path, @NotNull
SystemUser... systemUsers) throws Exception {
+ AclManager aclManager = new DefaultAclManager();
+ for (SystemUser systemUser : systemUsers) {
+ 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/java/org/apache/sling/feature/cpconverter/handlers/RepRepoPolicyEntryHandlerTest.java
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepRepoPolicyEntryHandlerTest.java
index f70c6f4..cf65878 100644
---
a/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepRepoPolicyEntryHandlerTest.java
+++
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/RepRepoPolicyEntryHandlerTest.java
@@ -29,6 +29,8 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
import java.io.StringReader;
import java.util.List;
@@ -67,11 +69,8 @@ public class RepRepoPolicyEntryHandlerTest {
String path = "/jcr_root/_rep_repoPolicy.xml";
AclManager aclManager = new DefaultAclManager();
aclManager.addSystemUser(new SystemUser("repolevel-service", new
RepoPath("/home/users/system/test"), new RepoPath("/home/users/system")));
- Extension repoinitExtension =
TestUtils.createRepoInitExtension(handler, aclManager, path,
getClass().getResourceAsStream(path.substring(1)));
-
- assertNotNull(repoinitExtension);
- assertEquals(ExtensionType.TEXT, repoinitExtension.getType());
- assertTrue(repoinitExtension.isRequired());
+ OutputStream out = new ByteArrayOutputStream();
+ Extension repoinitExtension = new
ParseResult(TestUtils.createRepoInitExtension(handler, aclManager, path,
getClass().getResourceAsStream(path.substring(1)), out),
out.toString()).getRepoinitExtension();
String expectedEnd =
"set ACL for repolevel-service" + System.lineSeparator() +
diff --git
a/src/test/java/org/apache/sling/feature/cpconverter/handlers/UsersEntryHandlerTest.java
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/UsersEntryHandlerTest.java
index b232330..5ba09b5 100644
---
a/src/test/java/org/apache/sling/feature/cpconverter/handlers/UsersEntryHandlerTest.java
+++
b/src/test/java/org/apache/sling/feature/cpconverter/handlers/UsersEntryHandlerTest.java
@@ -21,13 +21,22 @@ 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.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import java.io.StringReader;
import java.util.List;
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.accesscontrol.User;
import org.apache.sling.repoinit.parser.RepoInitParser;
import org.apache.sling.repoinit.parser.impl.RepoInitParserService;
import org.apache.sling.repoinit.parser.operations.Operation;
@@ -101,6 +110,15 @@ public class UsersEntryHandlerTest {
assertTrue(actual.contains("/home/users/system/my:feature"));
}
+ @Test
+ public void testUser() throws Exception {
+ String path = "/jcr_root/home/users/a/author/.content.xml";
+ AclManager aclManager = mock(AclManager.class);
+ TestUtils.createRepoInitExtension(usersEntryHandler, aclManager, path,
getClass().getResourceAsStream(path.substring(1)));
+ verify(aclManager, times(1)).addUser(any(User.class));
+ verify(aclManager, never()).addSystemUser(any(SystemUser.class));
+ }
+
private Extension parseAndSetRepoinit(String path) throws Exception {
return TestUtils.createRepoInitExtension(usersEntryHandler, new
DefaultAclManager(), path, getClass().getResourceAsStream(path.substring(1)));
}
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>