blob: 49d02ffa49adff6789e44479f804759bedb34587 [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.jackrabbit.oak.security.authorization.restriction;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import javax.jcr.Value;
import javax.jcr.security.AccessControlManager;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.apache.jackrabbit.oak.AbstractSecurityTest;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.ContentSession;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
import org.apache.jackrabbit.oak.util.NodeUtil;
import org.apache.jackrabbit.value.StringValue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class PermissionTest extends AbstractSecurityTest {
private static final String TEST_ROOT_PATH = "/testRoot";
private static final String TEST_A_PATH = "/testRoot/a";
private static final String TEST_B_PATH = "/testRoot/a/b";
private static final String TEST_C_PATH = "/testRoot/a/b/c";
private static final String TEST_D_PATH = "/testRoot/a/b/c/d";
private static final String TEST_E_PATH = "/testRoot/a/b/c/d/e";
private Principal testPrincipal;
@Before
@Override
public void before() throws Exception {
super.before();
NodeUtil rootNode = new NodeUtil(root.getTree("/"));
NodeUtil testRootNode = rootNode.addChild("testRoot", NT_UNSTRUCTURED);
NodeUtil a = testRootNode.addChild("a", NT_UNSTRUCTURED);
NodeUtil b = a.addChild("b", NT_UNSTRUCTURED);
NodeUtil c = b.addChild("c", NT_UNSTRUCTURED);
NodeUtil d = c.addChild("d", NT_UNSTRUCTURED);
d.addChild("e", NT_UNSTRUCTURED);
root.commit();
testPrincipal = getTestUser().getPrincipal();
}
@After
@Override
public void after() throws Exception {
try {
// revert uncommitted changes
root.refresh();
// remove all test content
root.getTree(TEST_ROOT_PATH).remove();
root.commit();
} finally {
super.after();
}
}
private void addEntry(String path, boolean grant, String restriction, String... privilegeNames) throws Exception {
AccessControlManager acMgr = getAccessControlManager(root);
JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, path);
if (restriction.length() > 0) {
Map<String, Value> rs = new HashMap<String, Value>();
rs.put("rep:glob", new StringValue(restriction));
acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), grant, rs);
} else {
acl.addEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), grant);
}
acMgr.setPolicy(path, acl);
root.commit();
}
private void assertIsGranted(PermissionProvider pp, Root root, boolean allow, String path, long permissions) {
assertEquals("user should " + (allow ? "" : "not ") + "have " + permissions + " on " + path,
allow, pp.isGranted(root.getTree(path), null, permissions));
}
private PermissionProvider getPermissionProvider(ContentSession session) {
return getSecurityProvider()
.getConfiguration(AuthorizationConfiguration.class)
.getPermissionProvider(root, session.getWorkspaceName(), session.getAuthInfo().getPrincipals());
}
@Test
public void testHasPermission() throws Exception {
// create permissions
// allow rep:write /testroot
// allow jcr:removeNode /testroot/a/b
// deny jcr:removeNode /testroot/a/b/c
addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
addEntry(TEST_B_PATH, true, "", PrivilegeConstants.JCR_REMOVE_NODE);
addEntry(TEST_C_PATH, false, "", PrivilegeConstants.JCR_REMOVE_NODE);
ContentSession testSession = createTestSession();
try {
Root testRoot = testSession.getLatestRoot();
PermissionProvider pp = getPermissionProvider(testSession);
assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
try {
testRoot.getTree(TEST_C_PATH).remove();
testRoot.commit();
fail("removing node on /a/b/c should fail");
} catch (CommitFailedException e) {
// all ok
}
} finally {
testSession.close();
}
}
/**
* Tests if the restrictions are properly inherited.
* the restriction enable/disable the ACE where it is defined.
* since the 'allow' on /a/b is after the 'deny' on a/b/c, the allow wins.
*
* The test currently fails on evaluation of /a/b/c/d. Probably because the evaluation
* of /a/b/c yields a deny, which terminates the iteration.
*/
@Test
public void testHasPermissionWithRestrictions() throws Exception {
// create permissions
// allow rep:write /testroot
// deny jcr:removeNode /testroot/a glob=*/c
// allow jcr:removeNode /testroot/a glob=*/b
addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
addEntry(TEST_A_PATH, false, "*/c", PrivilegeConstants.JCR_REMOVE_NODE);
addEntry(TEST_A_PATH, true, "*/b", PrivilegeConstants.JCR_REMOVE_NODE);
ContentSession testSession = createTestSession();
try {
Root testRoot = testSession.getLatestRoot();
PermissionProvider pp = getPermissionProvider(testSession);
assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, true, TEST_D_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, true, TEST_E_PATH, Permissions.REMOVE_NODE);
// should be able to remove /a/b/c/d
testRoot.getTree(TEST_D_PATH).remove();
testRoot.commit();
// should be able to remove /a/b/c
try {
testRoot.getTree(TEST_C_PATH).remove();
testRoot.commit();
fail("user should not be able to remove c");
} catch (CommitFailedException e) {
// ok
}
} finally {
testSession.close();
}
}
/**
* Tests if the restrictions are properly inherited.
* the restriction enable/disable the ACE where it is defined.
* since the 'deny' on /a/b is after the 'allow' on a/b/c, the deny wins.
*/
@Test
public void testHasPermissionWithRestrictions2() throws Exception {
// create permissions
// allow rep:write /testroot
// allow jcr:removeNode /testroot/a glob=*/b
// deny jcr:removeNode /testroot/a glob=*/c
addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
addEntry(TEST_A_PATH, true, "*/b", PrivilegeConstants.JCR_REMOVE_NODE);
addEntry(TEST_A_PATH, false, "*/c", PrivilegeConstants.JCR_REMOVE_NODE);
ContentSession testSession = createTestSession();
try {
Root testRoot = testSession.getLatestRoot();
PermissionProvider pp = getPermissionProvider(testSession);
assertIsGranted(pp, testRoot, true, TEST_A_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.REMOVE_NODE);
assertIsGranted(pp, testRoot, true, TEST_D_PATH, Permissions.REMOVE_NODE);
testRoot.getTree(TEST_D_PATH).remove();
testRoot.commit();
try {
// should not be able to remove /a/b/c
testRoot.getTree(TEST_C_PATH).remove();
testRoot.commit();
fail("should not be able to delete " + TEST_C_PATH);
} catch (CommitFailedException e) {
// ok
testRoot.refresh();
}
} finally {
testSession.close();
}
}
/**
* Tests the custom restriction provider that checks on the existence of a property.
* @throws Exception
*/
@Test
public void testProtectPropertiesByRestriction() throws Exception {
// create permissions
// allow rep:write /testroot
// deny jcr:modifyProperties /testroot/a glob = */c
addEntry(TEST_ROOT_PATH, true, "", PrivilegeConstants.JCR_READ, PrivilegeConstants.REP_WRITE);
addEntry(TEST_A_PATH, false, "*/c", PrivilegeConstants.JCR_MODIFY_PROPERTIES);
ContentSession testSession = createTestSession();
try {
Root testRoot = testSession.getLatestRoot();
PermissionProvider pp = getPermissionProvider(testSession);
assertIsGranted(pp, testRoot, true , TEST_A_PATH, Permissions.MODIFY_PROPERTY);
assertIsGranted(pp, testRoot, true, TEST_B_PATH, Permissions.MODIFY_PROPERTY);
assertIsGranted(pp, testRoot, false, TEST_C_PATH, Permissions.MODIFY_PROPERTY);
assertIsGranted(pp, testRoot, true, TEST_D_PATH, Permissions.MODIFY_PROPERTY);
assertIsGranted(pp, testRoot, true, TEST_E_PATH, Permissions.MODIFY_PROPERTY);
} finally {
testSession.close();
}
}
}