blob: 913226b299dca40b4f66935458075ac21eb9b360 [file] [log] [blame]
/*
* 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.api.JackrabbitSession;
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.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
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.AclUtil;
import org.apache.sling.jcr.repoinit.impl.RepoInitException;
import org.apache.sling.jcr.repoinit.impl.TestUtil;
import org.apache.sling.repoinit.parser.RepoInitParsingException;
import org.apache.sling.repoinit.parser.operations.AclLine;
import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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.jcr.security.AccessControlPolicy;
import javax.jcr.security.Privilege;
import javax.security.auth.Subject;
import java.security.Principal;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.UUID;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class PrincipalBasedAclTest {
private static final String PRINCIPAL_BASED_SUBTREE = "principalbased";
private static final String REMOVE_NOT_SUPPORTED_REGEX = ".*REMOVE[a-zA-Z ]+not supported.*";
@Rule
public final OsgiContext context = new OsgiContext();
private Repository repository;
private JackrabbitSession adminSession;
private JackrabbitAccessControlManager acMgr;
private TestUtil U;
private String path;
private String propPath;
private String relPath;
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 = (JackrabbitSession) repository.login(new SimpleCredentials(uid, uid.toCharArray()), null);
acMgr = AclUtil.getJACM(adminSession);
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();
relPath = sp.getParameters(UserConfiguration.NAME).getConfigValue(UserConstants.PARAM_SYSTEM_RELATIVE_PATH, UserConstants.DEFAULT_SYSTEM_RELATIVE_PATH) + "/" + PRINCIPAL_BASED_SUBTREE;
U.parseAndExecute("create service user " + U.username + " with path " + relPath);
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, PRINCIPAL_BASED_SUBTREE)));
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 = () -> systemUserPrincipalName;
Subject subject = new Subject(true, Collections.singleton(principal), Collections.emptySet(), Collections.emptySet());
return Subject.doAs(subject, (PrivilegedExceptionAction<Session>) () -> 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));
}
private static void assertRegex(String regex, String shouldMatch) {
assertTrue("Expecting '" + shouldMatch + "'' to match " + regex, shouldMatch.matches(regex));
}
@NotNull
private static PrincipalAccessControlList assertPolicy(@NotNull Principal principal, @NotNull Session session, int expectedSize) throws RepositoryException {
PrincipalAccessControlList acl = getAcl(principal, session);
assertNotNull(acl);
assertEquals(expectedSize, acl.size());
return acl;
}
private Authorizable getServiceUser(@NotNull String uid) throws RepositoryException {
UserManager uMgr = adminSession.getUserManager();
Authorizable a = uMgr.getAuthorizable(uid);
if (a != null) {
return a;
} else {
throw new RepositoryException("Expected service user " + uid + " to exist.");
}
}
private Principal getPrincipal(@NotNull String serviceUserId) throws RepositoryException {
return getServiceUser(serviceUserId).getPrincipal();
}
@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 = RepoInitException.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 = RepoInitException.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 = RepoInitException.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 with path " + relPath);
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);
}
@Test
public void redundantEntry() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:read on "+path+"\n"
+ "allow jcr:read on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
Principal principal = adminSession.getUserManager().getAuthorizable(U.username).getPrincipal();
JackrabbitAccessControlManager acMgr = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
PrincipalAccessControlList pacl = null;
for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
if (policy instanceof PrincipalAccessControlList) {
pacl = (PrincipalAccessControlList) policy;
break;
}
}
assertNotNull(pacl);
// an identical entry should only be present once
assertEquals(1, pacl.size());
}
@Test
public void grantWithSecondSetup() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:read on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
Principal principal = adminSession.getUserManager().getAuthorizable(U.username).getPrincipal();
JackrabbitAccessControlManager acMgr = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
PrincipalAccessControlList pacl = null;
for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
if (policy instanceof PrincipalAccessControlList) {
pacl = (PrincipalAccessControlList) policy;
break;
}
}
assertNotNull(pacl);
assertEquals(2, pacl.size());
}
@Test
public void principalAclNotAvailable() throws Exception {
try {
// create service user outside of supported tree for principal-based access control
U.parseAndExecute("create service user otherSystemPrincipal");
String setup = "set principal ACL for otherSystemPrincipal \n"
+ "allow jcr:read on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
} finally {
U.cleanupServiceUser("otherSystemPrincipal");
}
}
@Test
public void principalAclNotAvailableRestrictionMismatch() throws Exception {
JackrabbitAccessControlManager acMgr = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
try {
// create service user outside of supported tree for principal-based access control
U.parseAndExecute("create service user otherSystemPrincipal");
// setup path-based access control to establish effective permission setup
String setup = "set ACL for otherSystemPrincipal \n"
+ "allow jcr:read on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
Principal principal = adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
assertTrue(acMgr.hasPrivileges(path, Collections.singleton(principal), AccessControlUtils.privilegesFromNames(adminSession, Privilege.JCR_READ)));
setup = "set principal ACL for otherSystemPrincipal \n"
+ "allow jcr:read on " + path + " restriction(rep:glob,*mismatch)\n"
+ "end";
U.parseAndExecute(setup);
} finally {
U.cleanupServiceUser("otherSystemPrincipal");
}
}
@Test
public void principalAclNotAvailableEntryPresent() throws Exception {
JackrabbitAccessControlManager acMgr = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
try {
// create service user outside of supported tree for principal-based access control
U.parseAndExecute("create service user otherSystemPrincipal");
// setup path-based access control to establish effective permission setup
String setup = "set ACL for otherSystemPrincipal \n"
+ "allow jcr:read on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
Principal principal = adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
assertTrue(acMgr.hasPrivileges(path, Collections.singleton(principal), AccessControlUtils.privilegesFromNames(adminSession, Privilege.JCR_READ)));
// setting up principal-acl will not succeed (principal not located below supported path)
// but there exists an effective entry with the same definition -> no exception
setup = "set principal ACL for otherSystemPrincipal \n"
+ "allow jcr:read on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
assertFalse(policy instanceof PrincipalAccessControlList);
}
} finally {
U.cleanupServiceUser("otherSystemPrincipal");
}
}
@Test
public void principalAclNotAvailableEntryWithRestrictionPresent() throws Exception {
JackrabbitAccessControlManager acMgr = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
try {
// create service user outside of supported tree for principal-based access control
U.parseAndExecute("create service user otherSystemPrincipal");
// setup path-based access control to establish effective permission setup
String setup = "set ACL for otherSystemPrincipal \n"
+ "allow jcr:read on " + path + " restriction(rep:glob,*abc*)\n"
+ "end";
U.parseAndExecute(setup);
// setting up principal-acl will not succeed (principal not located below supported path)
// but there exists an equivalent entry with the same definition -> no exception
setup = "set principal ACL for otherSystemPrincipal \n"
+ "allow jcr:read on " + path + " restriction(rep:glob,*abc*)\n"
+ "end";
U.parseAndExecute(setup);
Principal principal = adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
assertFalse(policy instanceof PrincipalAccessControlList);
}
} finally {
U.cleanupServiceUser("otherSystemPrincipal");
}
}
@Test
public void principalAclNotAvailableRepoLevelPermissions() throws Exception {
JackrabbitAccessControlManager acMgr = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
try {
// create service user outside of supported tree for principal-based access control
U.parseAndExecute("create service user otherSystemPrincipal");
// setup path-based access control to establish effective permission setup
String setup = "set ACL for otherSystemPrincipal \n"
+ "allow jcr:namespaceManagement on :repository\n"
+ "end";
U.parseAndExecute(setup);
// setting up principal-acl will not succeed (principal not located below supported path)
// but there exists an equivalent entry with the same definition -> no exception
setup = "set principal ACL for otherSystemPrincipal \n"
+ "allow jcr:namespaceManagement on :repository\n"
+ "end";
U.parseAndExecute(setup);
Principal principal = adminSession.getUserManager().getAuthorizable("otherSystemPrincipal").getPrincipal();
for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
assertFalse(policy instanceof PrincipalAccessControlList);
}
} finally {
U.cleanupServiceUser("otherSystemPrincipal");
}
}
@Test
public void principalAclNotAvailableNonExistingNode() throws Exception {
JackrabbitAccessControlManager acMgr = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
try {
// create service user outside of supported tree for principal-based access control
U.parseAndExecute("create service user otherSystemPrincipal");
// setting up principal-acl will not succeed (principal not located below supported path)
// but since the target node does not exist we cannot verify if an equivalent resource-based ac-setup exists
// (AccessControlManager.getPolicies would fail with PathNotFoundException) => relaxed behavior (SLING-9412)
String setup = "set principal ACL for otherSystemPrincipal \n"
+ "allow jcr:read on /non/existing/path\n"
+ "end";
U.parseAndExecute(setup);
Principal principal = getPrincipal("otherSystemPrincipal");
for (AccessControlPolicy policy : acMgr.getPolicies(principal)) {
assertFalse(policy instanceof PrincipalAccessControlList);
}
} finally {
U.cleanupServiceUser("otherSystemPrincipal");
}
}
@Test
public void testHomePath() throws Exception {
Authorizable su = getServiceUser(U.username);
Principal principal = su.getPrincipal();
assertNull(getAcl(principal, U.adminSession));
AclLine line = new AclLine(AclLine.Action.ALLOW);
line.setProperty(AclLine.PROP_PRINCIPALS, Collections.singletonList(principal.getName()));
line.setProperty(AclLine.PROP_PRIVILEGES, Collections.singletonList(Privilege.JCR_READ));
line.setProperty(AclLine.PROP_PATHS, Collections.singletonList(":home:"+U.username+"#"));
AclUtil.setPrincipalAcl(U.adminSession, U.username, Collections.singletonList(line));
PrincipalAccessControlList acl = getAcl(principal, U.adminSession);
assertNotNull(acl);
assertEquals(1, acl.size());
PrincipalAccessControlList.Entry entry = (PrincipalAccessControlList.Entry) acl.getAccessControlEntries()[0];
assertArrayEquals(AccessControlUtils.privilegesFromNames(AclUtil.getJACM(U.adminSession), Privilege.JCR_READ), entry.getPrivileges());
assertEquals(su.getPath(), entry.getEffectivePath());
}
@Test
public void testTransientUser() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
String id = "systemUser_" + UUID.randomUUID().toString();
try {
User su = uMgr.createSystemUser(id, relPath);
String setup = "set principal ACL for "+su.getPrincipal().getName()+" \n"
+ "allow jcr:read on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
PrincipalAccessControlList acl = getAcl(su.getPrincipal(), U.adminSession);
assertNotNull(acl);
assertEquals(1, acl.size());
} finally {
U.adminSession.refresh(false);
Authorizable a = uMgr.getAuthorizable(id);
if (a != null) {
a.remove();
U.adminSession.save();
}
}
}
@Test
public void testRemoveAction() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
setup = "set principal ACL for " + U.username + "\n"
+ "remove jcr:write on " + path + "\n"
+ "end";
try {
U.parseAndExecute(setup);
fail("Expecting REMOVE to fail");
} catch(RuntimeException rex) {
assertRegex(REMOVE_NOT_SUPPORTED_REGEX, rex.getMessage());
}
}
@Test
public void testRemoveNoExistingPolicy() throws Exception {
String setup = "remove principal ACE for " + U.username + "\n"
+ "allow jcr:read on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
}
@Test
public void testRemoveMatchingEntry() throws Exception {
Principal principal = getPrincipal(U.username);
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(principal, U.adminSession, 1);
setup = "remove principal ACE for " + U.username + "\n"
+ "allow jcr:write on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(principal, U.adminSession, 0);
}
@Test
public void testRemoveNoMatchingEntry() throws Exception {
Principal principal = getPrincipal(U.username);
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(principal, U.adminSession, 1);
// privilege mismatch
setup = "remove principal ACE for " + U.username + "\n"
+ "allow jcr:read on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(principal, U.adminSession, 1);
// privilege mismatch 2
setup = "remove principal ACE for " + U.username + "\n"
+ "allow jcr:read,jcr:write on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(principal, U.adminSession, 1);
// path mismatch
setup = "remove principal ACE for " + U.username + "\n"
+ "allow jcr:write on " + path + "/mismatch\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(principal, U.adminSession, 1);
// restriction mismatch
setup = "remove principal ACE for " + U.username + "\n"
+ "allow jcr:write on " + path + " restriction(rep:glob, /*/jcr:content/*)\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(principal, U.adminSession, 1);
}
@Test(expected = RepoInitException.class)
public void testRemoveNonExistingPrincipal() throws Exception {
String setup = "remove principal ACE for nonExistingPrincipal\n"
+ "deny jcr:write on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
}
@Test
public void testRemovePrincipalMismatch() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
U.parseAndExecute("create service user otherSystemPrincipal");
assertPolicy(getPrincipal(U.username), U.adminSession, 1);
setup = "remove principal ACE for otherSystemPrincipal\n"
+ "allow jcr:write on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
// ace must not have been removed
assertPolicy(getPrincipal(U.username), U.adminSession, 1);
assertNull(getAcl(getPrincipal("otherSystemPrincipal"), U.adminSession));
}
@Test
public void testRemoveAllNoExistingPolicy() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "remove * on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
// removal must be ignored and no policy must be created
assertNull(getAcl(getPrincipal(U.username), U.adminSession));
}
@Test(expected = RepoInitException.class)
public void testAllRemoveNonExistingPrincipal() throws Exception {
String setup = "set principal ACL for nonExistingPrincipal\n"
+ "remove * on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
}
@Test
public void testRemoveAll() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "allow jcr:read on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
setup = "set principal ACL for " + U.username + "\n"
+ "remove * on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 0);
}
@Test
public void testRemoveAllRepositoryPath() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "allow jcr:namespaceManagement on :repository\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 2);
setup = "set principal ACL for " + U.username + "\n"
+ "remove * on :repository\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 1);
}
@Test
public void testRemoveAllPartialPathMatch() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "allow jcr:write on /another/path\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 2);
setup = "set principal ACL for " + U.username + "\n"
+ "remove * on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 1);
}
@Test
public void testRemoveAllMultiplePaths() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "allow jcr:write on home("+U.username+")\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 2);
setup = "set principal ACL for " + U.username + "\n"
+ "remove * on " + path + ", home("+U.username+")\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 0);
}
@Test
public void testRemoveAllPathMismatch() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "allow jcr:read on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
setup = "set principal ACL for " + U.username + "\n"
+ "remove * on /another/path\n"
+ "end";
U.parseAndExecute(setup);
assertPolicy(getPrincipal(U.username), U.adminSession, 2);
}
@Test
public void testRemoveAllPrincipalMismatch() throws Exception {
String setup = "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "end";
U.parseAndExecute(setup);
U.parseAndExecute("create service user otherSystemPrincipal");
setup = "set principal ACL for otherSystemPrincipal\n"
+ "remove * on " + path + "\n"
+ "end";
U.parseAndExecute(setup);
}
@Test
public void testRemoveAllPrincipalDuplicated() throws Exception {
U.parseAndExecute(""
+ "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "allow jcr:read on "+path+"\n"
+ "end\n"
+ "set principal ACL for " + U.username + "\n"
// duplicating the remove statement exposes SLING-10145
+ "remove * on " + path + "\n"
+ "remove * on " + path + "\n"
+ "end\n"
);
assertPolicy(getPrincipal(U.username), U.adminSession, 0);
}
@Test
public void testAddAndDeleteAcl() throws Exception {
U.parseAndExecute(""
+ "set principal ACL for " + U.username + "\n"
+ "allow jcr:write on "+path+"\n"
+ "end\n"
+ "delete principal ACL for " + U.username + "\n"
);
assertNull(getAcl(getPrincipal(U.username), U.adminSession));
}
@Test
public void testDeleteAcl() throws Exception {
PrincipalAccessControlList acl = getApplicableAcl(getPrincipal(U.username), U.adminSession);
acl.addEntry("/content", AccessControlUtils.privilegesFromNames(U.adminSession, Privilege.JCR_READ));
U.adminSession.getAccessControlManager().setPolicy(acl.getPath(), acl);
U.adminSession.save();
assertNotNull(getAcl(getPrincipal(U.username), U.adminSession));
U.parseAndExecute("delete principal ACL for " + U.username + "\n");
assertNull(getAcl(getPrincipal(U.username), U.adminSession));
}
@Test
public void testDeleteNonExistingAcl() throws Exception {
assertNull(getAcl(getPrincipal(U.username), U.adminSession));
// removing non-existing policy must not fail
U.parseAndExecute("delete principal ACL for " + U.username + "\n");
assertNull(getAcl(getPrincipal(U.username), U.adminSession));
}
@Test
public void testDeleteAclNonExistingPrincipal() throws Exception {
assertNull(getAcl(getPrincipal(U.username), U.adminSession));
// removing policy for non-existing principal must not fail
U.parseAndExecute("delete principal ACL for non-existing-service-user\n");
assertNull(getAcl(getPrincipal(U.username), U.adminSession));
}
@Test
public void testDeleteResourceBasedAclByPrincipal() throws Exception {
U.parseAndExecute(""
+ "create path (nt:unstructured) /var\n"
+ "set ACL for " + U.username + "\n"
+ "allow jcr:read on /var\n"
+ "end\n"
+ "set principal ACL for " + U.username + "\n"
+ "allow jcr:read on /var\n"
+ "end\n");
assertEquals(1, acMgr.getPolicies("/var").length);
U.parseAndExecute("delete ACL for "+U.username+"\n");
// resource-based acl at /var must be removed as it only contains a single entry for U.userName
assertEquals(0, acMgr.getPolicies("/var").length);
// removing resource-based ac-setup by principal must not delete any principal-based ac setup.
Principal p = getPrincipal(U.username);
AccessControlPolicy[] policies = acMgr.getPolicies(p);
assertEquals(1, policies.length);
assertTrue(policies[0] instanceof PrincipalAccessControlList);
}
@Nullable
private static PrincipalAccessControlList getApplicableAcl(@NotNull Principal principal, @NotNull Session session) throws RepositoryException {
for (AccessControlPolicy policy : AclUtil.getJACM(session).getApplicablePolicies(principal)) {
if (policy instanceof PrincipalAccessControlList) {
return (PrincipalAccessControlList) policy;
}
}
return null;
}
@Nullable
private static PrincipalAccessControlList getAcl(@NotNull Principal principal, @NotNull Session session) throws RepositoryException {
for (AccessControlPolicy policy : AclUtil.getJACM(session).getPolicies(principal)) {
if (policy instanceof PrincipalAccessControlList) {
return (PrincipalAccessControlList) policy;
}
}
return null;
}
}