blob: 7a434f7e02885d93ee1a1f12fe3e0866f75f3c4f [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.impl;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.user.Group;
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.spi.security.principal.EveryonePrincipal;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
import org.apache.sling.repoinit.parser.RepoInitParsingException;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.apache.sling.testing.mock.sling.junit.SlingContext;
import org.junit.After;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
public class AclUtilTest {
@Rule
public final SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK);
private TestUtil U;
private JackrabbitAccessControlList acl;
@Before
public void setup() throws RepositoryException, RepoInitParsingException {
U = new TestUtil(context);
U.parseAndExecute("create service user " + U.username);
final String aclSetup =
"set ACL for " + U.username + "\n"
+ "allow jcr:read on /\n"
+ "allow jcr:write on /\n"
+ "end"
;
U.parseAndExecute(aclSetup);
acl = AccessControlUtils.getAccessControlList(U.adminSession, "/");
}
@After
public void cleanup() throws RepositoryException, RepoInitParsingException {
if (U.adminSession != null) {
U.adminSession.refresh(false);
}
U.cleanupUser();
}
@Test
public void entryIsContained() throws Exception {
// validates that an exact match of the username, privilege list and isAllow is contained
assertIsContained(acl, U.username, new String[]{ Privilege.JCR_READ, Privilege.JCR_WRITE }, true);
}
@Test
public void testDeclaredAggregatePrivilegesAreContained() throws Exception {
String [] privileges = new String[]{
//JCR_READ
"rep:readNodes","rep:readProperties",
// JCR_WRITE
Privilege.JCR_ADD_CHILD_NODES,Privilege.JCR_MODIFY_PROPERTIES,
Privilege.JCR_REMOVE_CHILD_NODES, Privilege.JCR_REMOVE_NODE
};
assertIsContained(acl, U.username, privileges, true);
}
@Test
public void testAllAggregatePrivilegesAreContained() throws Exception {
String [] privileges = new String[]{
//JCR_READ
"rep:readNodes","rep:readProperties",
// JCR_WRITE
Privilege.JCR_ADD_CHILD_NODES,"rep:addProperties",
"rep:alterProperties","rep:removeProperties",
Privilege.JCR_REMOVE_CHILD_NODES, Privilege.JCR_REMOVE_NODE
};
assertIsContained(acl, U.username, privileges, true);
}
@Test
public void entryWithFewerPrivilegesIsContained() throws Exception {
// validates that an exact match of the username and isAllow but with fewer privileges is contained
assertIsContained(acl, U.username, new String[]{ Privilege.JCR_WRITE }, true);
}
@Test
public void entryWithDifferentPrivilegeIsNotContained() throws Exception {
// validates that an exact match of the username and isAllow but with different privileges is contained
assertIsNotContained(acl, U.username, new String[]{ Privilege.JCR_ALL }, true);
}
@Test
public void entryWithPartiallyMatchingPrivilegesIsContained() throws Exception {
// validates that an exact match of the username and isAllow but with privileges partially overlapping is contained
// existing: JCR_READ, JCR_WRITE
// new: JCR_READ, JCR_MODIFY_PROPERTIES
assertIsContained(acl, U.username, new String[]{Privilege.JCR_READ, Privilege.JCR_MODIFY_PROPERTIES }, true);
}
@Test
public void entryWithDifferentUserIsNotContained() throws Exception {
// validates that an exact match of the privileges and isAllow but with different username is not contained
String otherPrincipalName = U.username + "_";
try {
U.parseAndExecute("create service user " + otherPrincipalName);
assertIsNotContained(acl, otherPrincipalName, new String[]{ Privilege.JCR_READ, Privilege.JCR_WRITE }, true);
} finally {
U.cleanupServiceUser(otherPrincipalName);
}
}
@Test
public void entryWithDifferentIsAllowIsNotContained() throws Exception {
// validates that an exact match of the username and privileges but with different is allow is not contained
assertIsNotContained(acl, U.username, new String[]{ Privilege.JCR_READ, Privilege.JCR_WRITE }, false);
}
private void assertArrayCompare(Object[] a, Object[] b, boolean expected) {
final boolean actual = AclUtil.compareArrays(a, b);
assertEquals("Expecting compareArrays to return " + expected, actual, expected);
}
@Test
public void compareArraysTest() {
final String[] a1 = { "a", "b" };
final String[] a2 = { "a", "b" };
final String[] a3 = { "b", "c" };
final String[] a4 = { "a", "b", "c" };
final String[] a5 = { "b", "a" };
final String[] a6 = { "b", "a", "c" };
final String[] emptyA = {};
final String[] emptyB = {};
assertArrayCompare(null, null, true);
assertArrayCompare(null, a1, false);
assertArrayCompare(a2, null, false);
assertArrayCompare(a1, a2, true);
assertArrayCompare(a1, a3, false);
assertArrayCompare(a3, a1, false);
assertArrayCompare(a1, a3, false);
assertArrayCompare(a1, a4, false);
assertArrayCompare(a4, a4, true);
assertArrayCompare(a1, a5, true);
assertArrayCompare(a4, a6, true);
assertArrayCompare(emptyA, emptyB, true);
}
/**
* Repo init should work for principals even if they are not present as user/group with the user manager.
*
* @see <a href="https://issues.apache.org/jira/browse/SLING-8604">SLING-8604</>
*/
@Test
public void testSetAclForEveryone() throws Exception {
Session session = U.adminSession;
try {
assertTrue(session instanceof JackrabbitSession);
assertNull(((JackrabbitSession) session).getUserManager().getAuthorizable(EveryonePrincipal.getInstance()));
AclUtil.setAcl(session, Collections.singletonList(EveryonePrincipal.NAME), Collections.singletonList(PathUtils.ROOT_PATH), Collections.singletonList(Privilege.JCR_READ), false);
assertIsContained(AccessControlUtils.getAccessControlList(session, PathUtils.ROOT_PATH), EveryonePrincipal.NAME, new String[] {Privilege.JCR_READ}, false);
} finally {
session.refresh(false);
}
}
/**
* @see <a href="https://issues.apache.org/jira/browse/SLING-8604">SLING-8604</>
*/
@Test
public void testSetAclForPrincipalNameDifferentFromId() throws Exception {
U.cleanupUser();
try {
Principal principal = new PrincipalImpl(U.id + "_principalName");
assertTrue(U.adminSession instanceof JackrabbitSession);
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
uMgr.createUser(U.username, null, principal, null);
U.adminSession.save();
AclUtil.setAcl(U.adminSession, Collections.singletonList(principal.getName()), Collections.singletonList(PathUtils.ROOT_PATH), Collections.singletonList(Privilege.JCR_READ), false);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, PathUtils.ROOT_PATH), principal.getName(), new String[] {Privilege.JCR_READ}, false);
} finally {
U.adminSession.refresh(false);
}
}
/**
* Test backwards compatibility of SLING-8604: test that the fallback mechanism still tries to
* resolve the given 'principalName' to an authorizable by treating it as the identifier, if there exists no principal
* for the given name (i.e ID is not equal to the principal name.
*
* @see <a href="https://issues.apache.org/jira/browse/SLING-8604">SLING-8604</>
*/
@Test
public void testSetAclUsingUserId() throws Exception {
U.cleanupUser();
try {
Principal principal = new PrincipalImpl(U.id + "_principalName");
assertTrue(U.adminSession instanceof JackrabbitSession);
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
uMgr.createUser(U.username, null, principal, null);
U.adminSession.save();
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), Collections.singletonList(PathUtils.ROOT_PATH), Collections.singletonList(Privilege.JCR_READ), false);
// assert that the entries is created for the principal name (and not the identifier)
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, PathUtils.ROOT_PATH), principal.getName(), new String[] {Privilege.JCR_READ}, false);
// assert no entry has been created for the identifier (which cannot be resolved to a principal)
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, PathUtils.ROOT_PATH), U.username, new String[] {Privilege.JCR_READ}, false);
} finally {
U.adminSession.refresh(false);
}
}
@Test
public void testSetAclWithHomePath() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
String userHomePath = uMgr.getAuthorizable(U.username).getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
List<String> paths = Collections.singletonList(":home:"+U.username+"#");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
}
@Test
public void testSetAclWithHomePathMultipleIds() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
String userHomePath = uMgr.getAuthorizable(U.username).getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
Group gr = uMgr.createGroup("groupId");
String groupHomePath = gr.getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
List<String> paths = Collections.singletonList(":home:"+U.username+","+gr.getID()+"#");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
}
@Test
public void testSetAclWithHomePathMultiplePath() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
String userHomePath = uMgr.getAuthorizable(U.username).getPath();
List<String> paths = Arrays.asList(":home:" + U.username + "#", ":repository", PathUtils.ROOT_PATH);
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_ALL), true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_ALL}, true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, null), U.username, new String[] {Privilege.JCR_ALL}, true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, PathUtils.ROOT_PATH), U.username, new String[] {Privilege.JCR_ALL}, true);
}
@Test
public void testSetAclWithHomePathAndSubtree() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
String userHomePath = U.adminSession.getNode(uMgr.getAuthorizable(U.username).getPath()).addNode("profiles").addNode("private").getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
Group gr = uMgr.createGroup("groupId");
String groupHomePath = U.adminSession.getNode(gr.getPath()).addNode("profiles").addNode("private").getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
List<String> paths = Collections.singletonList(":home:"+U.username+","+gr.getID()+"#/profiles/private");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
}
@Test(expected = PathNotFoundException.class)
public void testSetAclWithHomePathAndMissingSubtree() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
String userHomePath = uMgr.getAuthorizable(U.username).getPath() + "/profiles/private";
assertFalse(U.adminSession.nodeExists(userHomePath));
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, userHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
Group gr = uMgr.createGroup("groupId");
String groupHomePath = U.adminSession.getNode(gr.getPath()).addNode("profiles").addNode("private").getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
List<String> paths = Collections.singletonList(":home:"+U.username+","+gr.getID()+"#/profiles/private");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
}
@Test
public void testSetAclWithHomePathIdWithHash() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
Group gr = uMgr.createGroup("g#roupId#");
String groupHomePath = gr.getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
List<String> paths = Collections.singletonList(":home:"+gr.getID()+","+U.username+"#");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
}
@Test
public void testSetAclWithHomePathIdWithColon() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
Group gr = uMgr.createGroup(":group:Id");
String groupHomePath = gr.getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
List<String> paths = Collections.singletonList(":home:"+gr.getID()+","+U.username+"#");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
}
@Ignore("TODO: user/groupId containing , will fail ac setup")
@Test
public void testSetAclWithHomePathIdWithComma() throws Exception {
UserManager uMgr = ((JackrabbitSession) U.adminSession).getUserManager();
Group gr = uMgr.createGroup(",group,Id,");
String groupHomePath = gr.getPath();
assertIsNotContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
List<String> paths = Collections.singletonList(":home:"+gr.getID()+","+U.username+"#");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
assertIsContained(AccessControlUtils.getAccessControlList(U.adminSession, groupHomePath), U.username, new String[] {Privilege.JCR_READ}, true);
}
@Test(expected = IllegalStateException.class)
public void testSetAclWithHomePathMissingTrailingHash() throws Exception {
List<String> paths = Collections.singletonList(":home:"+U.username);
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
}
@Test(expected = PathNotFoundException.class)
public void testSetAclWithHomePathUnknownUser() throws Exception {
List<String> paths = Collections.singletonList(":home:alice#");
AclUtil.setAcl(U.adminSession, Collections.singletonList(U.username), paths, Collections.singletonList(Privilege.JCR_READ), true);
}
private void assertIsContained(JackrabbitAccessControlList acl, String username, String[] privilegeNames, boolean isAllow) throws RepositoryException {
assertIsContained0(acl, username, privilegeNames, isAllow, true);
}
private void assertIsNotContained(JackrabbitAccessControlList acl, String username, String[] privilegeNames, boolean isAllow) throws RepositoryException {
assertIsContained0(acl, username, privilegeNames, isAllow, false);
}
private void assertIsContained0(JackrabbitAccessControlList acl, String username, String[] privilegeNames, boolean isAllow, boolean contained) throws RepositoryException {
AclUtil.LocalAccessControlEntry localAce = new AclUtil.LocalAccessControlEntry(principal(username), privileges(privilegeNames), isAllow);
if ( contained ) {
assertTrue("ACL does not contain an entry for " + localAce, AclUtil.contains(acl.getAccessControlEntries(), localAce));
} else {
assertFalse("ACL contains an entry for " + localAce, AclUtil.contains(acl.getAccessControlEntries(), localAce));
}
}
private Principal principal(String principalName) throws RepositoryException {
return AccessControlUtils.getPrincipal(U.adminSession, principalName);
}
private Privilege[] privileges(String... privilegeNames) throws RepositoryException {
return AccessControlUtils.privilegesFromNames(U.adminSession, privilegeNames);
}
}