This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git
The following commit(s) were added to refs/heads/master by this push:
new c26f293 SLING-8602 - Add support for PrincipalAccessControlList and
ac-management by principal
c26f293 is described below
commit c26f293e465469f8b46fa2469e757d11138136e7
Author: Angela Schreiber <[email protected]>
AuthorDate: Fri Sep 20 10:56:18 2019 +0200
SLING-8602 - Add support for PrincipalAccessControlList and ac-management
by principal
---
pom.xml | 19 +
.../apache/sling/jcr/repoinit/impl/AclUtil.java | 56 +++
.../apache/sling/jcr/repoinit/impl/AclVisitor.java | 13 +
.../sling/jcr/repoinit/impl/DoNothingVisitor.java | 5 +
.../sling/jcr/repoinit/PrincipalBasedAclTest.java | 393 +++++++++++++++++++++
.../apache/sling/jcr/repoinit/impl/TestUtil.java | 6 +
6 files changed, 492 insertions(+)
diff --git a/pom.xml b/pom.xml
index 9db292d..2e83a88 100644
--- a/pom.xml
+++ b/pom.xml
@@ -264,7 +264,26 @@
</dependency>
<dependency>
<groupId>org.apache.jackrabbit</groupId>
+ <artifactId>oak-security-spi</artifactId>
+ <version>${oak.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>oak-core</artifactId>
+ <version>${oak.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jackrabbit</groupId>
<artifactId>oak-core</artifactId>
+ <classifier>tests</classifier>
+ <version>${oak.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>oak-authorization-principalbased</artifactId>
<version>${oak.version}</version>
<scope>provided</scope>
</dependency>
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java
b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java
index 4128dbb..da409d1 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclUtil.java
@@ -18,6 +18,7 @@ package org.apache.sling.jcr.repoinit.impl;
import java.security.Principal;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -30,12 +31,15 @@ import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFactory;
import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlException;
import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import
org.apache.jackrabbit.api.security.authorization.PrincipalAccessControlList;
import org.apache.jackrabbit.api.security.user.Authorizable;
import
org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.apache.sling.repoinit.parser.operations.AclLine;
@@ -43,6 +47,9 @@ import
org.apache.sling.repoinit.parser.operations.RestrictionClause;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.apache.sling.repoinit.parser.operations.AclLine.PROP_PATHS;
+import static
org.apache.sling.repoinit.parser.operations.AclLine.PROP_PRIVILEGES;
+
/** Utilities for ACL management */
public class AclUtil {
@@ -155,6 +162,55 @@ public class AclUtil {
setAcl(session, principals, (String)null, privileges, isAllow,
restrictionClauses);
}
+ public static void setPrincipalAcl(Session session, String principalName,
Collection<AclLine> lines) throws RepositoryException {
+ JackrabbitAccessControlManager acMgr = getJACM(session);
+ Principal principal = AccessControlUtils.getPrincipal(session,
principalName);
+ checkState(principal != null, "Principal not found: " + principalName);
+
+ PrincipalAccessControlList acl = getPrincipalAccessControlList(acMgr,
principal);
+ boolean modified = false;
+ for (AclLine line : lines) {
+ if (line.getAction() == AclLine.Action.DENY) {
+ throw new AccessControlException("PrincipalAccessControlList
doesn't support 'deny' entries.");
+ }
+ LocalRestrictions restrictions =
createLocalRestrictions(line.getRestrictions(), acl, session);
+ Privilege[] privileges =
AccessControlUtils.privilegesFromNames(session,
line.getProperty(PROP_PRIVILEGES).toArray(new String[0]));
+
+ for (String path : line.getProperty(PROP_PATHS)) {
+ String effectivePath = (path == null || path.isEmpty() ||
AclLine.PATH_REPOSITORY.equals(path)) ? null : path;
+ boolean added = acl.addEntry(effectivePath, privileges,
restrictions.getRestrictions(), restrictions.getMVRestrictions());
+ if (!added) {
+ LOG.info("Equivalent principal-based entry already exists
for principal {} and effective path {} ", principalName, path);
+ } else {
+ modified = true;
+ }
+ }
+ }
+ if (modified) {
+ acMgr.setPolicy(acl.getPath(), acl);
+ }
+ }
+
+ private static PrincipalAccessControlList
getPrincipalAccessControlList(JackrabbitAccessControlManager acMgr, Principal
principal) throws RepositoryException {
+ PrincipalAccessControlList acl = null;
+ for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
+ if (policy instanceof PrincipalAccessControlList) {
+ acl = (PrincipalAccessControlList) policy;
+ break;
+ }
+ }
+ if (acl == null) {
+ for (AccessControlPolicy policy :
acMgr.getApplicablePolicies(principal)) {
+ if (policy instanceof PrincipalAccessControlList) {
+ acl = (PrincipalAccessControlList) policy;
+ break;
+ }
+ }
+ }
+ checkState(acl != null, "No PrincipalAccessControlList available for
principal " + principal);
+ return acl;
+ }
+
// visible for testing
static boolean contains(AccessControlEntry[] existingAces,
LocalAccessControlEntry newAce) throws RepositoryException {
for (int i = 0 ; i < existingAces.length; i++) {
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java
b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java
index 8cc392d..b1a0c57 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/AclVisitor.java
@@ -30,6 +30,7 @@ import org.apache.sling.repoinit.parser.operations.CreatePath;
import org.apache.sling.repoinit.parser.operations.PathSegmentDefinition;
import org.apache.sling.repoinit.parser.operations.RestrictionClause;
import org.apache.sling.repoinit.parser.operations.SetAclPaths;
+import org.apache.sling.repoinit.parser.operations.SetAclPrincipalBased;
import org.apache.sling.repoinit.parser.operations.SetAclPrincipals;
/** OperationVisitor which processes only operations related to ACLs.
@@ -99,6 +100,18 @@ class AclVisitor extends DoNothingVisitor {
}
@Override
+ public void visitSetAclPrincipalBased(SetAclPrincipalBased s) {
+ for (String principalName : s.getPrincipals()) {
+ try {
+ log.info("Adding principal-based access control entry for {}",
principalName);
+ AclUtil.setPrincipalAcl(session, principalName, s.getLines());
+ } catch(Exception e) {
+ throw new RuntimeException("Failed to set principal-based ACL
(" + e.getMessage() + ")", e);
+ }
+ }
+ }
+
+ @Override
public void visitCreatePath(CreatePath cp) {
String parentPath = "";
for(PathSegmentDefinition psd : cp.getDefinitions()) {
diff --git
a/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
b/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
index 340fc62..7a28010 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
@@ -31,6 +31,7 @@ import
org.apache.sling.repoinit.parser.operations.RegisterNamespace;
import org.apache.sling.repoinit.parser.operations.RegisterNodetypes;
import org.apache.sling.repoinit.parser.operations.RegisterPrivilege;
import org.apache.sling.repoinit.parser.operations.SetAclPaths;
+import org.apache.sling.repoinit.parser.operations.SetAclPrincipalBased;
import org.apache.sling.repoinit.parser.operations.SetAclPrincipals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -88,6 +89,10 @@ class DoNothingVisitor implements OperationVisitor {
}
@Override
+ public void visitSetAclPrincipalBased(SetAclPrincipalBased operation) {
+ }
+
+ @Override
public void visitCreatePath(CreatePath cp) {
}
diff --git
a/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
b/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
new file mode 100644
index 0000000..ad42344
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/repoinit/PrincipalBasedAclTest.java
@@ -0,0 +1,393 @@
+/*
+ * 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.jcr.repoinit;
+
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import
org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.security.internal.SecurityProviderBuilder;
+import org.apache.jackrabbit.oak.security.internal.SecurityProviderHelper;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import
org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import
org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.FilterProviderImpl;
+import
org.apache.jackrabbit.oak.spi.security.authorization.principalbased.impl.PrincipalBasedAuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.principal.SystemUserPrincipal;
+import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
+import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
+import org.apache.sling.jcr.repoinit.impl.TestUtil;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.security.AccessControlManager;
+import javax.security.auth.Subject;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PrincipalBasedAclTest {
+
+ @Rule
+ public final OsgiContext context = new OsgiContext();
+
+ private Repository repository;
+ private Session adminSession;
+
+ private TestUtil U;
+
+ private String path;
+ private String propPath;
+
+ private Session testSession;
+
+ @Before
+ public void before() throws Exception {
+ SecurityProvider sp = createSecurityProvider();
+ repository = new Jcr().with(sp).createRepository();
+
+ String uid =
sp.getParameters(UserConfiguration.NAME).getConfigValue(UserConstants.PARAM_ADMIN_ID,
UserConstants.DEFAULT_ADMIN_ID);
+ adminSession = repository.login(new SimpleCredentials(uid,
uid.toCharArray()), null);
+ U = new TestUtil(adminSession);
+
+ Node tmp = adminSession.getRootNode().addNode("tmp_" + U.id);
+ Property prop = tmp.setProperty("prop", "value");
+ path = tmp.getPath();
+ propPath = prop.getPath();
+ adminSession.save();
+
+ U.parseAndExecute("create service user " + U.username);
+
+ testSession = loginSystemUserPrincipal(U.username);
+
+ assertPermission(testSession, PathUtils.ROOT_PATH,
Session.ACTION_READ, false);
+ assertPermission(testSession, path, Session.ACTION_READ, false);
+
+ }
+
+ @After
+ public void after() throws Exception {
+ try {
+ adminSession.removeItem(path);
+ adminSession.save();
+ U.cleanupUser();
+ } finally {
+ adminSession.logout();
+ testSession.logout();
+ if (repository instanceof JackrabbitRepository) {
+ ((JackrabbitRepository) repository).shutdown();
+ }
+ }
+ }
+
+ private SecurityProvider createSecurityProvider() {
+ /*
+ * use composition-type OR in order in order to simplify the
permission setup. since the tests in this class
+ * don't setup permissions using the default authorization, the
OR-setup will be sufficient to check for permissions
+ * granted with the principal-based module only.
+ *
+ * in an AND setup scenario one would need to inject the aggregation
filter defined by oak-authorization-princialbased
+ */
+ SecurityProvider sp =
SecurityProviderBuilder.newBuilder().with(ConfigurationParameters.of("authorizationCompositionType",
"OR")).build();
+
+ ConfigurationParameters userParams =
sp.getParameters(UserConfiguration.NAME);
+ String userRoot =
userParams.getConfigValue(UserConstants.PARAM_USER_PATH,
UserConstants.DEFAULT_USER_PATH);
+ String systemRelPath =
userParams.getConfigValue(UserConstants.PARAM_SYSTEM_RELATIVE_PATH,
UserConstants.DEFAULT_SYSTEM_RELATIVE_PATH);
+ FilterProviderImpl fp = new FilterProviderImpl();
+ context.registerInjectActivateService(fp,
Collections.singletonMap("path", PathUtils.concat(userRoot, systemRelPath)));
+
+ PrincipalBasedAuthorizationConfiguration authorizationConfig = new
PrincipalBasedAuthorizationConfiguration();
+
authorizationConfig.bindMountInfoProvider(Mounts.defaultMountInfoProvider());
+ authorizationConfig.bindFilterProvider(fp);
+ SecurityProviderHelper.updateConfig(sp, authorizationConfig,
AuthorizationConfiguration.class);
+ return sp;
+ }
+
+ /**
+ * Create a JCR Session for the given system principal that is based on a
Subject that only contains SystemUserPrincipal(s).
+ * The result from U.loginService with result in a Session that
additionally contains group-membership, which is not
+ * supported by the default FilterProvider implementation (i.e. access
control setup and permission evaluation is not supported
+ * by the PrincipalBasedAuthorizationConfiguration as needed for these
tests).
+ *
+ * @param systemUserPrincipalName Name of a system user principal
+ * @return A JCR Session
+ * @throws Exception If the repository login fails.
+ */
+ private Session loginSystemUserPrincipal(final String
systemUserPrincipalName) throws Exception {
+ SystemUserPrincipal principal = new SystemUserPrincipal() {
+ @Override
+ public String getName() {
+ return systemUserPrincipalName;
+ }
+ };
+ Subject subject = new Subject(true, Collections.singleton(principal),
Collections.emptySet(), Collections.emptySet());
+ return Subject.doAs(subject, new PrivilegedExceptionAction<Session>() {
+ @Override
+ public Session run() throws Exception {
+ return repository.login(null, null);
+ }
+ });
+ }
+
+ private static void assertPermission(Session userSession, String
absolutePath, String actions, boolean successExpected) throws
RepositoryException {
+ assertEquals("Expecting "+actions+" for path " + absolutePath + " to
be " + (successExpected ? "granted" : "denied"), successExpected,
userSession.hasPermission(absolutePath, actions));
+ }
+
+ @Test
+ public void readGranted() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on " + path + "\n"
+ + "end";
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, path, Session.ACTION_READ, true);
+ assertPermission(testSession, propPath, Session.ACTION_READ, true);
+ assertPermission(testSession, propPath, Session.ACTION_SET_PROPERTY,
false);
+ }
+
+ @Test
+ public void grantedNonExistingPath() throws Exception {
+ String nonExistingPath = path +"/nonExisting";
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on " + nonExistingPath + "\n"
+ + "end";
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, nonExistingPath, Session.ACTION_READ,
true);
+ }
+
+ @Test
+ public void grantAtRootPath() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:all on /\n"
+ + "end"
+ ;
+
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, "/newChild", Session.ACTION_READ +","+
Session.ACTION_ADD_NODE +","+ Session.ACTION_REMOVE, true);
+ }
+
+ @Test
+ public void multiplePrivileges() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read, jcr:versionManagement on " + path +
"\n"
+ + "end";
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ String permissions =
Permissions.getString(Permissions.READ|Permissions.VERSION_MANAGEMENT);
+ assertPermission(testSession, path, permissions, true);
+ assertPermission(testSession, path + "/newchild", permissions, true);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void invalidPrivilege() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow my:invalidPrivilege on " + path + "\n"
+ + "end";
+ U.parseAndExecute(setup);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void denyEntry() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "deny jcr:read on " + path + "\n"
+ + "end";
+ U.parseAndExecute(setup);
+ }
+
+ @Test
+ public void repoLevelPermission() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:namespaceManagement on :repository\n"
+ + "end";
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertTrue(testSession.getAccessControlManager().hasPrivileges(null,
AccessControlUtils.privilegesFromNames(testSession,
"jcr:namespaceManagement")));
+ }
+
+ @Test
+ public void repoLevelAndPath() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:all on :repository, "+path+"\n"
+ + "end";
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ AccessControlManager acMgr = testSession.getAccessControlManager();
+ assertTrue(acMgr.hasPrivileges(path,
AccessControlUtils.privilegesFromNames(testSession, "jcr:all")));
+ assertTrue(acMgr.hasPrivileges(null,
AccessControlUtils.privilegesFromNames(testSession, "jcr:all")));
+ }
+
+ @Test
+ public void globRestriction() throws Exception {
+ String notAllowed = "/testxyz_" + U.id;
+ String allowed = "/testabc_" + U.id;
+
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on "+path+"
restriction(rep:glob,*abc*)\n"
+ + "end"
+ ;
+
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, path + allowed, Session.ACTION_READ,
true);
+ assertPermission(testSession, path + notAllowed, Session.ACTION_READ,
false);
+ }
+
+ @Test
+ public void emptyGlobRestriction() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on "+path+" restriction (rep:glob)\n"
+ + "end";
+
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, "/", Session.ACTION_READ, false);
+ assertPermission(testSession, path, Session.ACTION_READ, true);
+ assertPermission(testSession, propPath, Session.ACTION_READ, false);
+ assertPermission(testSession, "/path/child", Session.ACTION_READ,
false);
+ }
+
+ @Test
+ public void mvItemNamesRestriction() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:modifyProperties on /
restriction(rep:itemNames,propName,prop)\n"
+ + "end"
+ ;
+
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, propPath, Session.ACTION_SET_PROPERTY,
true);
+ assertPermission(testSession, path + "/propName",
Session.ACTION_SET_PROPERTY, true);
+ }
+
+
+ @Test
+ public void emptyMvRestrictionTest() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on "+path+"
restriction(rep:ntNames)\n"
+ + "end"
+ ;
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, propPath, Session.ACTION_READ, false);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void unsupportedRestriction() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on "+path+"
restriction(jcr:unsupported,value)\n"
+ + "end"
+ ;
+ U.parseAndExecute(setup);
+ }
+
+ @Test
+ public void multiplePaths() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on "+path+", /content\n"
+ + "end";
+
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, PathUtils.ROOT_PATH,
Session.ACTION_READ, false);
+ assertPermission(testSession, path, Session.ACTION_READ, true);
+ assertPermission(testSession, "/content", Session.ACTION_READ, true);
+ }
+
+ @Test(expected = RepoInitParsingException.class)
+ public void missingPath() throws Exception {
+ String setup =
+ "set principal ACL for " + U.username + "\n"
+ + "allow jcr:read on \n"
+ + "end";
+
+ U.parseAndExecute(setup);
+ }
+
+ @Test
+ public void multiplePrincipals() throws Exception {
+ Session s = null;
+ try {
+ U.parseAndExecute("create service user otherSystemPrincipal");
+ String setup =
+ "set principal ACL for " + U.username +
",otherSystemPrincipal \n"
+ + "allow jcr:read on " + path + "\n"
+ + "end";
+ U.parseAndExecute(setup);
+ testSession.refresh(false);
+
+ assertPermission(testSession, propPath, Session.ACTION_READ, true);
+ s = loginSystemUserPrincipal("otherSystemPrincipal");
+ assertPermission(s, propPath, Session.ACTION_READ, true);
+ } finally {
+ if (s != null) {
+ s.logout();
+ }
+ U.cleanupServiceUser("otherSystemPrincipal");
+ }
+ }
+
+ @Test(expected = RepoInitParsingException.class)
+ public void missingPrincipal() throws Exception {
+ String setup =
+ "set principal ACL for \n"
+ + "allow jcr:read on "+path+"\n"
+ + "end";
+
+ U.parseAndExecute(setup);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java
b/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java
index b1b86e9..9067a6d 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java
@@ -56,6 +56,12 @@ public class TestUtil {
username = "user_" + id;
}
+ public TestUtil(Session adminSession) {
+ this.adminSession = adminSession;
+ id = UUID.randomUUID().toString();
+ username = "user_" + id;
+ }
+
public List<Operation> parse(String input) throws RepoInitParsingException
{
final Reader r = new StringReader(input);
try {