/* * 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.core.security.authorization.acl; import java.security.Principal; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.UUID; import javax.jcr.AccessDeniedException; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.security.AccessControlList; import javax.jcr.security.AccessControlManager; import javax.jcr.security.AccessControlPolicy; import javax.jcr.security.AccessControlPolicyIterator; import javax.jcr.security.Privilege; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager; import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.core.security.TestPrincipal; import org.apache.jackrabbit.test.NotExecutableException; import org.apache.jackrabbit.test.api.security.AbstractAccessControlTest; /** * <code>EntryCollectorTest</code>... */ public class EntryCollectorTest extends AbstractAccessControlTest { private Group testGroup; private User testUser; private String path; private String childNPath; @Override protected void setUp() throws Exception { super.setUp(); // create some nodes below the test root in order to apply ac-stuff Node node = testRootNode.addNode(nodeName1, testNodeType); Node cn1 = node.addNode(nodeName2, testNodeType); superuser.save(); path = node.getPath(); childNPath = cn1.getPath(); // create the testGroup UserManager umgr = getUserManager(superuser); Principal groupPrincipal = new TestPrincipal("testGroup" + UUID.randomUUID()); testGroup = umgr.createGroup(groupPrincipal); testUser = umgr.createUser("testUser" + UUID.randomUUID(), "pw"); if (!umgr.isAutoSave() && superuser.hasPendingChanges()) { superuser.save(); } } @Override protected void tearDown() throws Exception { try { if (testGroup != null) { testGroup.remove(); if (!getUserManager(superuser).isAutoSave() && superuser.hasPendingChanges()) { superuser.save(); } } if (testUser != null) { testUser.remove(); if (!getUserManager(superuser).isAutoSave() && superuser.hasPendingChanges()) { superuser.save(); } } } finally { super.tearDown(); } } private static UserManager getUserManager(Session session) throws NotExecutableException { if (!(session instanceof JackrabbitSession)) { throw new NotExecutableException(); } try { return ((JackrabbitSession) session).getUserManager(); } catch (RepositoryException e) { throw new NotExecutableException(); } } private ACLTemplate getPolicy(AccessControlManager acM, String path, Principal principal) throws RepositoryException, AccessDeniedException, NotExecutableException { // try applicable (new) ACLs first AccessControlPolicyIterator itr = acM.getApplicablePolicies(path); while (itr.hasNext()) { AccessControlPolicy policy = itr.nextAccessControlPolicy(); if (policy instanceof ACLTemplate) { return (ACLTemplate) policy; } } // try if there is an acl that has been set before: AccessControlPolicy[] pcls = acM.getPolicies(path); for (AccessControlPolicy policy : pcls) { if (policy instanceof ACLTemplate) { return (ACLTemplate) policy; } } // no applicable or existing ACLTemplate to edit -> not executable. throw new NotExecutableException(); } private ACLTemplate modifyPrivileges(String path, Principal principal, Privilege[] privileges, boolean isAllow) throws NotExecutableException, RepositoryException { ACLTemplate tmpl = getPolicy(acMgr, path, principal); tmpl.addEntry(principal, privileges, isAllow); acMgr.setPolicy(tmpl.getPath(), tmpl); superuser.save(); return tmpl; } private static void verifyACEs(AccessControlPolicy[] policies, String policyPath, int numberOfAce) throws RepositoryException { JackrabbitAccessControlList acl = null; for (AccessControlPolicy p : policies) { if (p instanceof JackrabbitAccessControlList) { if (policyPath.equals(((JackrabbitAccessControlList) p).getPath())) { acl = (JackrabbitAccessControlList) p; } } } if (acl == null) { fail("No Jackrabbit ACL found at " + policyPath); } else { assertEquals(numberOfAce, acl.getAccessControlEntries().length); } } public void testCache() throws Exception { // --- test1 : add an ACE at path -------------------------------------- modifyPrivileges(path, testGroup.getPrincipal(), privilegesFromName(Privilege.JCR_READ), true); AccessControlPolicy[] plcs = acMgr.getEffectivePolicies(path); AccessControlPolicy[] plcs2 = acMgr.getEffectivePolicies(childNPath); // effective policies must be the equal on path and childPath assertTrue(Arrays.equals(plcs, plcs2)); // the policy at 'path' must contain a single ACE verifyACEs(plcs2, path, 1); // --- test2: modify the policy at 'path' ------------------------------ modifyPrivileges(path, testGroup.getPrincipal(), privilegesFromName(Privilege.JCR_WRITE), false); plcs = acMgr.getEffectivePolicies(path); plcs2 = acMgr.getEffectivePolicies(childNPath); // effective policies must be the equal on path and childNPath assertTrue(Arrays.equals(plcs, plcs2)); verifyACEs(plcs2, path, 2); // --- test3: add an policy at childNPath ------------------------------ modifyPrivileges(childNPath, testGroup.getPrincipal(), privilegesFromName(Privilege.JCR_ADD_CHILD_NODES), true); plcs = acMgr.getEffectivePolicies(path); plcs2 = acMgr.getEffectivePolicies(childNPath); assertFalse(Arrays.equals(plcs, plcs2)); verifyACEs(plcs2, path, 2); verifyACEs(plcs2, childNPath, 1); // --- test4: modify policy at childNPath ------------------------------ modifyPrivileges(childNPath, testGroup.getPrincipal(), privilegesFromName(Privilege.JCR_REMOVE_CHILD_NODES), true); plcs = acMgr.getEffectivePolicies(path); plcs2 = acMgr.getEffectivePolicies(childNPath); assertFalse(Arrays.equals(plcs, plcs2)); verifyACEs(plcs2, path, 2); // still a single ACE at childNPath. but privileges must be adjusted verifyACEs(plcs2, childNPath, 1); AccessControlList acl = null; for (AccessControlPolicy p : plcs2) { if (p instanceof JackrabbitAccessControlList && childNPath.equals(((JackrabbitAccessControlList) p).getPath())) { acl = (AccessControlList) p; } } Privilege[] privs = privilegesFromNames(new String[] {Privilege.JCR_ADD_CHILD_NODES, Privilege.JCR_REMOVE_CHILD_NODES}); assertEquals(privs, acl.getAccessControlEntries()[0].getPrivileges()); // --- test4: remove policy at childNPath ------------------------------ acMgr.removePolicy(childNPath, acMgr.getPolicies(childNPath)[0]); superuser.save(); plcs = acMgr.getEffectivePolicies(path); AccessControlPolicy[] plcs3 = acMgr.getEffectivePolicies(childNPath); assertTrue(Arrays.equals(plcs, plcs3)); assertFalse(Arrays.equals(plcs2, plcs3)); for (AccessControlPolicy p : plcs3) { if (p instanceof JackrabbitAccessControlList) { if (childNPath.equals(((JackrabbitAccessControlList) p).getPath())) { fail("Policy at path has been removed."); } } } verifyACEs(plcs, path, 2); } /** * Asserts that the given privilege sets are equal, regardless of ordering. */ private void assertEquals(Privilege[] expected, Privilege[] actual) { assertEquals(getPrivilegeNames(expected), getPrivilegeNames(actual)); } private Set<String> getPrivilegeNames(Privilege[] privileges) { Set<String> names = new HashSet<String>(); for (Privilege privilege : privileges) { names.add(privilege.getName()); } return names; } public void testPermissions() throws Exception { Session superuser2 = getHelper().getSuperuserSession(); try { JackrabbitAccessControlManager acM = (JackrabbitAccessControlManager) acMgr; JackrabbitAccessControlManager acM2 = (JackrabbitAccessControlManager) superuser2.getAccessControlManager(); Set<Principal> principals = Collections.singleton(testGroup.getPrincipal()); // --- test1 : add an ACE at path ---------------------------------- Privilege[] privs = privilegesFromName(Privilege.JCR_LOCK_MANAGEMENT); modifyPrivileges(path, testGroup.getPrincipal(), privs, true); assertTrue(acM.hasPrivileges(path, principals, privs)); assertTrue(acM2.hasPrivileges(path, principals, privs)); assertTrue(acM.hasPrivileges(childNPath, principals, privs)); assertTrue(acM2.hasPrivileges(childNPath, principals, privs)); // --- test2: modify the policy at 'path' ------------------------------ modifyPrivileges(path, testGroup.getPrincipal(), privilegesFromName(Privilege.JCR_WRITE), true); privs = privilegesFromNames(new String[] { Privilege.JCR_LOCK_MANAGEMENT, Privilege.JCR_WRITE}); assertTrue(acM.hasPrivileges(path, principals, privs)); assertTrue(acM2.hasPrivileges(path, principals, privs)); assertTrue(acM.hasPrivileges(childNPath, principals, privs)); assertTrue(acM2.hasPrivileges(childNPath, principals, privs)); // --- test3: add an policy at childNPath ------------------------------ modifyPrivileges(childNPath, testGroup.getPrincipal(), privilegesFromName(Privilege.JCR_ADD_CHILD_NODES), false); privs = privilegesFromNames(new String[] { Privilege.JCR_LOCK_MANAGEMENT, Privilege.JCR_WRITE}); assertTrue(acM.hasPrivileges(path, principals, privs)); assertTrue(acM2.hasPrivileges(path, principals, privs)); privs = privilegesFromNames(new String[] { Privilege.JCR_LOCK_MANAGEMENT, Privilege.JCR_MODIFY_PROPERTIES, Privilege.JCR_REMOVE_CHILD_NODES, Privilege.JCR_REMOVE_NODE}); assertTrue(acM.hasPrivileges(childNPath, principals, privs)); assertTrue(acM2.hasPrivileges(childNPath, principals, privs)); // --- test4: modify policy at childNPath -------------------------- modifyPrivileges(childNPath, testGroup.getPrincipal(), privilegesFromName(Privilege.JCR_REMOVE_CHILD_NODES), false); privs = privilegesFromNames(new String[] { Privilege.JCR_LOCK_MANAGEMENT, Privilege.JCR_WRITE}); assertTrue(acM.hasPrivileges(path, principals, privs)); assertTrue(acM2.hasPrivileges(path, principals, privs)); privs = privilegesFromNames(new String[] { Privilege.JCR_LOCK_MANAGEMENT, Privilege.JCR_MODIFY_PROPERTIES, Privilege.JCR_REMOVE_NODE}); assertTrue(acM.hasPrivileges(childNPath, principals, privs)); assertTrue(acM2.hasPrivileges(childNPath, principals, privs)); // --- test4: remove policy at childNPath -------------------------- acMgr.removePolicy(childNPath, acMgr.getPolicies(childNPath)[0]); superuser.save(); privs = privilegesFromNames(new String[] { Privilege.JCR_LOCK_MANAGEMENT, Privilege.JCR_WRITE}); assertTrue(acM.hasPrivileges(path, principals, privs)); assertTrue(acM2.hasPrivileges(path, principals, privs)); assertTrue(acM.hasPrivileges(childNPath, principals, privs)); assertTrue(acM2.hasPrivileges(childNPath, principals, privs)); } finally { superuser2.logout(); } } static interface TestInvokation { public void runTest() throws Exception; } private void runTestUnderLoad(TestInvokation ti) throws Exception { JcrTestThread t[] = new JcrTestThread[4]; for (int i = 0; i < t.length; i++) { t[i] = new JcrTestThread(); } try { for (int i = 0; i < t.length; i++) { t[i].start(); } ti.runTest(); } finally { for (int i = 0; i < t.length; i++) { t[i].stopMe(); t[i].join(); Throwable th = t[i].getLastExc(); if (th != null) { fail("failure in load thread: " + th); } } } } public void testCacheUnderLoad() throws Exception { runTestUnderLoad(new TestInvokation() { public void runTest() throws Exception { testCache(); } }); } public void testPermissionsUnderLoad() throws Exception { runTestUnderLoad(new TestInvokation() { public void runTest() throws Exception { testPermissions(); } }); } /** * Test code that that walks the repository. */ private class JcrTestThread extends Thread { private boolean stopme = false; private Throwable lastErr; @Override public void run() { while (!this.stopme) { Session session = null; try { session = getHelper().getReadOnlySession(); walk(session.getRootNode()); } catch (RepositoryException ex) { // ignored } catch (Throwable ex) { lastErr = ex; } finally { if (session != null) { session.logout(); session = null; } } } } public void stopMe() { this.stopme = true; } public Throwable getLastExc() { return lastErr; } private void walk(Node node) { if (stopme) { return; } try { if ("/jcr:system".equals(node.getPath())) { // do not descend into an non-interesting subtree return; } NodeIterator ni = node.getNodes(); while (ni.hasNext()) { walk(ni.nextNode()); } } catch (RepositoryException ex) { // ignore } catch (Throwable ex) { lastErr = ex; } } } }