/* * 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 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.core.SessionImpl; import org.apache.jackrabbit.core.security.authorization.AbstractWriteTest; import org.apache.jackrabbit.core.security.authorization.AccessControlConstants; import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry; import org.apache.jackrabbit.core.security.principal.EveryonePrincipal; import org.apache.jackrabbit.core.security.TestPrincipal; import org.apache.jackrabbit.test.NotExecutableException; import org.apache.jackrabbit.util.Text; import javax.jcr.AccessDeniedException; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.security.AccessControlManager; import javax.jcr.security.AccessControlPolicy; import javax.jcr.security.AccessControlPolicyIterator; import javax.jcr.security.Privilege; import javax.jcr.security.AccessControlEntry; import java.security.Principal; import java.util.Collections; import java.util.Map; import java.util.UUID; /** * <code>EvaluationTest</code>... */ public class WriteTest extends AbstractWriteTest { @Override protected boolean isExecutable() { return EvaluationUtil.isExecutable(acMgr); } @Override protected JackrabbitAccessControlList getPolicy(AccessControlManager acM, String path, Principal principal) throws RepositoryException, AccessDeniedException, NotExecutableException { return EvaluationUtil.getPolicy(acM, path, principal); } @Override protected Map<String, Value> getRestrictions(Session s, String path) { return Collections.emptyMap(); } public void testAccessControlModification2() throws RepositoryException, NotExecutableException { /* precondition: testuser must have READ-only permission on test-node and below */ checkReadOnly(path); // give 'testUser' READ_AC|MODIFY_AC privileges at 'path' Privilege[] privileges = privilegesFromNames(new String[] { Privilege.JCR_READ_ACCESS_CONTROL, Privilege.JCR_MODIFY_ACCESS_CONTROL }); JackrabbitAccessControlList tmpl = givePrivileges(path, privileges, getRestrictions(superuser, path)); /* testuser must - still have the inherited READ permission. - must have permission to view AC items at 'path' (and below) - must have permission to modify AC items at 'path' testuser must not have - permission to view AC items outside of the tree defined by path. */ // make sure the 'rep:policy' node has been created. assertTrue(superuser.itemExists(tmpl.getPath() + "/rep:policy")); Session testSession = getTestSession(); AccessControlManager testAcMgr = getTestACManager(); // test: MODIFY_AC granted at 'path' assertTrue(testAcMgr.hasPrivileges(path, privilegesFromName(Privilege.JCR_MODIFY_ACCESS_CONTROL))); // test if testuser can READ access control on the path and on the // entire subtree that gets the policy inherited. AccessControlPolicy[] policies = testAcMgr.getPolicies(path); testAcMgr.getPolicies(childNPath); // test: READ_AC privilege does not apply outside of the tree. try { testAcMgr.getPolicies(siblingPath); fail("READ_AC privilege must not apply outside of the tree it has applied to."); } catch (AccessDeniedException e) { // success } // test: MODIFY_AC privilege does not apply outside of the tree. try { testAcMgr.setPolicy(siblingPath, policies[0]); fail("MODIFY_AC privilege must not apply outside of the tree it has applied to."); } catch (AccessDeniedException e) { // success } // test if testuser can modify AC-items // 1) add an ac-entry ACLTemplate acl = (ACLTemplate) policies[0]; acl.addAccessControlEntry(testUser.getPrincipal(), privilegesFromName(PrivilegeRegistry.REP_WRITE)); testAcMgr.setPolicy(path, acl); testSession.save(); assertTrue(testAcMgr.hasPrivileges(path, privilegesFromName(Privilege.JCR_REMOVE_CHILD_NODES))); // 2) remove the policy testAcMgr.removePolicy(path, policies[0]); testSession.save(); // Finally: testuser removed the policy that granted him permission // to modify the AC content. Since testuser removed the policy, it's // privileges must be gone again... try { testAcMgr.getEffectivePolicies(childNPath); fail("READ_AC privilege has been revoked -> must throw again."); } catch (AccessDeniedException e) { // success } // ... and since the ACE is stored with the policy all right except // READ must be gone. checkReadOnly(path); } public void testRemovePermission9() throws NotExecutableException, RepositoryException { AccessControlManager testAcMgr = getTestACManager(); /* precondition: testuser must have READ-only permission on test-node and below */ checkReadOnly(path); checkReadOnly(childNPath); Privilege[] rmChildNodes = privilegesFromName(Privilege.JCR_REMOVE_CHILD_NODES); Privilege[] rmNode = privilegesFromName(Privilege.JCR_REMOVE_NODE); // add 'remove_child_nodes' at 'path and allow 'remove_node' at childNPath givePrivileges(path, rmChildNodes, getRestrictions(superuser, path)); givePrivileges(childNPath, rmNode, getRestrictions(superuser, childNPath)); /* expected result: - rep:policy node can still not be remove for it is access-control content that requires jcr:modifyAccessControl privilege instead. */ String policyPath = childNPath + "/rep:policy"; assertFalse(getTestSession().hasPermission(policyPath, javax.jcr.Session.ACTION_REMOVE)); assertTrue(testAcMgr.hasPrivileges(policyPath, new Privilege[] {rmChildNodes[0], rmNode[0]})); } public void testApplicablePolicies() throws RepositoryException { AccessControlPolicyIterator it = acMgr.getApplicablePolicies(childNPath); assertTrue(it.hasNext()); // the same should be true, if the rep:AccessControllable mixin has // been manually added Node n = (Node) superuser.getItem(childNPath); n.addMixin(((SessionImpl) superuser).getJCRName(AccessControlConstants.NT_REP_ACCESS_CONTROLLABLE)); it = acMgr.getApplicablePolicies(childNPath); assertTrue(it.hasNext()); } public void testInheritance2() throws RepositoryException, NotExecutableException { Session testSession = getTestSession(); AccessControlManager testAcMgr = getTestACManager(); /* precondition: testuser must have READ-only permission on test-node and below */ checkReadOnly(path); checkReadOnly(childNPath); // give jcr:write privilege on 'path' and withdraw them on 'childNPath' Privilege[] privileges = privilegesFromNames(new String[] {Privilege.JCR_WRITE}); givePrivileges(path, privileges, getRestrictions(superuser, path)); withdrawPrivileges(childNPath, privileges, getRestrictions(superuser, path)); /* since evaluation respects inheritance through the node hierarchy, the jcr:write privilege must not be granted at childNPath */ assertFalse(testAcMgr.hasPrivileges(childNPath, privileges)); /* ... same for permissions at 'childNPath' */ String actions = Session.ACTION_SET_PROPERTY + "," + Session.ACTION_REMOVE + "," + Session.ACTION_ADD_NODE; String nonExistingItemPath = childNPath + "/anyItem"; assertFalse(testSession.hasPermission(nonExistingItemPath, actions)); // yet another level in the hierarchy Node grandChild = superuser.getNode(childNPath).addNode(nodeName3); superuser.save(); String gcPath = grandChild.getPath(); // grant write privilege again givePrivileges(gcPath, privileges, getRestrictions(superuser, path)); assertTrue(testAcMgr.hasPrivileges(gcPath, privileges)); assertTrue(testSession.hasPermission(gcPath + "/anyProp", Session.ACTION_SET_PROPERTY)); // however: removing the grand-child nodes must not be allowed as // remove_child_node privilege is missing on the direct ancestor. assertFalse(testSession.hasPermission(gcPath, Session.ACTION_REMOVE)); } public void testInheritedGroupPermissions() throws NotExecutableException, RepositoryException { Group testGroup = getTestGroup(); AccessControlManager testAcMgr = getTestACManager(); /* precondition: testuser must have READ-only permission on test-node and below */ checkReadOnly(path); Privilege[] privileges = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); /* give MODIFY_PROPERTIES privilege for testGroup at 'path' */ givePrivileges(path, testGroup.getPrincipal(), privileges, getRestrictions(superuser, path)); /* withdraw MODIFY_PROPERTIES privilege for everyone at 'childNPath' */ withdrawPrivileges(childNPath, EveryonePrincipal.getInstance(), privileges, getRestrictions(superuser, path)); // result at 'child path' must be deny assertFalse(testAcMgr.hasPrivileges(childNPath, privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES))); } public void testInheritedGroupPermissions2() throws NotExecutableException, RepositoryException { Group testGroup = getTestGroup(); AccessControlManager testAcMgr = getTestACManager(); /* precondition: testuser must have READ-only permission on test-node and below */ checkReadOnly(path); Privilege[] privileges = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); // NOTE: same as testInheritedGroupPermissions above but using // everyone on path, testgroup on childpath -> result must be the same /* give MODIFY_PROPERTIES privilege for everyone at 'path' */ givePrivileges(path, EveryonePrincipal.getInstance(), privileges, getRestrictions(superuser, path)); /* withdraw MODIFY_PROPERTIES privilege for testGroup at 'childNPath' */ withdrawPrivileges(childNPath, testGroup.getPrincipal(), privileges, getRestrictions(superuser, path)); // result at 'child path' must be deny assertFalse(testAcMgr.hasPrivileges(childNPath, privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES))); } public void testMultipleGroupPermissionsOnNode() throws NotExecutableException, RepositoryException { Group testGroup = getTestGroup(); /* create a second group the test user is member of */ Principal principal = new TestPrincipal("testGroup" + UUID.randomUUID()); UserManager umgr = getUserManager(superuser); Group group2 = umgr.createGroup(principal); try { group2.addMember(testUser); if (!umgr.isAutoSave() && superuser.hasPendingChanges()) { superuser.save(); } /* add privileges for the Group the test-user is member of */ Privilege[] privileges = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); givePrivileges(path, testGroup.getPrincipal(), privileges, getRestrictions(superuser, path)); withdrawPrivileges(path, group2.getPrincipal(), privileges, getRestrictions(superuser, path)); /* testuser must get the permissions/privileges inherited from the group it is member of. the denial of group2 must succeed */ String actions = javax.jcr.Session.ACTION_SET_PROPERTY + "," + javax.jcr.Session.ACTION_READ; AccessControlManager testAcMgr = getTestACManager(); assertFalse(getTestSession().hasPermission(path, actions)); Privilege[] privs = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); assertFalse(testAcMgr.hasPrivileges(path, privs)); } finally { group2.remove(); } } public void testMultipleGroupPermissionsOnNode2() throws NotExecutableException, RepositoryException { Group testGroup = getTestGroup(); /* create a second group the test user is member of */ Principal principal = new TestPrincipal("testGroup" + UUID.randomUUID()); UserManager umgr = getUserManager(superuser); Group group2 = umgr.createGroup(principal); try { group2.addMember(testUser); if (!umgr.isAutoSave() && superuser.hasPendingChanges()) { superuser.save(); } /* add privileges for the Group the test-user is member of */ Privilege[] privileges = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); withdrawPrivileges(path, testGroup.getPrincipal(), privileges, getRestrictions(superuser, path)); givePrivileges(path, group2.getPrincipal(), privileges, getRestrictions(superuser, path)); /* testuser must get the permissions/privileges inherited from the group it is member of. granting permissions for group2 must be effective */ String actions = javax.jcr.Session.ACTION_SET_PROPERTY + "," + javax.jcr.Session.ACTION_READ; AccessControlManager testAcMgr = getTestACManager(); assertTrue(getTestSession().hasPermission(path, actions)); Privilege[] privs = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); assertTrue(testAcMgr.hasPrivileges(path, privs)); } finally { group2.remove(); } } public void testReorderGroupPermissions() throws NotExecutableException, RepositoryException { Group testGroup = getTestGroup(); /* create a second group the test user is member of */ Principal principal = new TestPrincipal("testGroup" + UUID.randomUUID()); UserManager umgr = getUserManager(superuser); Group group2 = umgr.createGroup(principal); try { group2.addMember(testUser); if (!umgr.isAutoSave() && superuser.hasPendingChanges()) { superuser.save(); } /* add privileges for the Group the test-user is member of */ Privilege[] privileges = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); withdrawPrivileges(path, testGroup.getPrincipal(), privileges, getRestrictions(superuser, path)); givePrivileges(path, group2.getPrincipal(), privileges, getRestrictions(superuser, path)); /* testuser must get the permissions/privileges inherited from the group it is member of. granting permissions for group2 must be effective */ String actions = javax.jcr.Session.ACTION_SET_PROPERTY + "," + javax.jcr.Session.ACTION_READ; AccessControlManager testAcMgr = getTestACManager(); assertTrue(getTestSession().hasPermission(path, actions)); Privilege[] privs = privilegesFromName(Privilege.JCR_MODIFY_PROPERTIES); assertTrue(testAcMgr.hasPrivileges(path, privs)); // reorder the ACEs AccessControlEntry srcEntry = null; AccessControlEntry destEntry = null; JackrabbitAccessControlList acl = (JackrabbitAccessControlList) acMgr.getPolicies(path)[0]; for (AccessControlEntry entry : acl.getAccessControlEntries()) { Principal princ = entry.getPrincipal(); if (testGroup.getPrincipal().equals(princ)) { destEntry = entry; } else if (group2.getPrincipal().equals(princ)) { srcEntry = entry; } } acl.orderBefore(srcEntry, destEntry); acMgr.setPolicy(path, acl); superuser.save(); /* after reordering the permissions must be denied */ assertFalse(getTestSession().hasPermission(path, actions)); assertFalse(testAcMgr.hasPrivileges(path, privs)); } finally { group2.remove(); } } public void testWriteIfReadingParentIsDenied() throws Exception { Privilege[] privileges = privilegesFromNames(new String[] {Privilege.JCR_READ, Privilege.JCR_WRITE}); /* deny READ/WRITE privilege for testUser at 'path' */ withdrawPrivileges(path, testUser.getPrincipal(), privileges, getRestrictions(superuser, path)); /* allow READ/WRITE privilege for testUser at 'childNPath' */ givePrivileges(childNPath, testUser.getPrincipal(), privileges, getRestrictions(superuser, childNPath)); Session testSession = getTestSession(); assertFalse(testSession.nodeExists(path)); // reading the node and it's definition must succeed. assertTrue(testSession.nodeExists(childNPath)); Node n = testSession.getNode(childNPath); n.addNode("someChild"); n.save(); } public void testRemoveNodeWithPolicy() throws Exception { Privilege[] privileges = privilegesFromNames(new String[] {Privilege.JCR_READ, Privilege.JCR_WRITE}); /* allow READ/WRITE privilege for testUser at 'path' */ givePrivileges(path, testUser.getPrincipal(), privileges, getRestrictions(superuser, path)); /* allow READ/WRITE privilege for testUser at 'childPath' */ givePrivileges(childNPath, testUser.getPrincipal(), privileges, getRestrictions(superuser, path)); Session testSession = getTestSession(); assertTrue(testSession.nodeExists(childNPath)); assertTrue(testSession.hasPermission(childNPath, Session.ACTION_REMOVE)); Node n = testSession.getNode(childNPath); // removing the child node must succeed as both remove-node and // remove-child-nodes are granted to testsession. // the policy node underneath childNPath should silently be removed // as the editing session has no knowledge about it's existence. n.remove(); testSession.save(); } public void testRemoveNodeWithInvisibleChild() throws Exception { Privilege[] privileges = privilegesFromNames(new String[] {Privilege.JCR_READ, Privilege.JCR_WRITE}); Node invisible = superuser.getNode(childNPath).addNode(nodeName3); superuser.save(); /* allow READ/WRITE privilege for testUser at 'path' */ givePrivileges(path, testUser.getPrincipal(), privileges, getRestrictions(superuser, path)); /* deny READ privilege at invisible node. (removal is still granted) */ withdrawPrivileges(invisible.getPath(), testUser.getPrincipal(), privilegesFromNames(new String[] {Privilege.JCR_READ}), getRestrictions(superuser, path)); Session testSession = getTestSession(); assertTrue(testSession.nodeExists(childNPath)); assertTrue(testSession.hasPermission(childNPath, Session.ACTION_REMOVE)); Node n = testSession.getNode(childNPath); // removing the child node must succeed as both remove-node and // remove-child-nodes are granted to testsession. // the policy node underneath childNPath should silently be removed // as the editing session has no knowledge about it's existence. n.remove(); testSession.save(); } public void testRemoveNodeWithInvisibleNonRemovableChild() throws Exception { Privilege[] privileges = privilegesFromNames(new String[] {Privilege.JCR_READ, Privilege.JCR_WRITE}); Node invisible = superuser.getNode(childNPath).addNode(nodeName3); superuser.save(); /* allow READ/WRITE privilege for testUser at 'path' */ givePrivileges(path, testUser.getPrincipal(), privileges, getRestrictions(superuser, path)); /* deny READ privilege at invisible node. (removal is still granted) */ withdrawPrivileges(invisible.getPath(), testUser.getPrincipal(), privileges, getRestrictions(superuser, path)); Session testSession = getTestSession(); assertTrue(testSession.nodeExists(childNPath)); assertTrue(testSession.hasPermission(childNPath, Session.ACTION_REMOVE)); Node n = testSession.getNode(childNPath); // removing the child node must fail as a hidden child node cannot // be removed. try { n.remove(); testSession.save(); fail(); } catch (AccessDeniedException e) { // success } } // https://issues.apache.org/jira/browse/JCR-3131 public void testEmptySaveNoRootAccess() throws RepositoryException, NotExecutableException { Session s = getTestSession(); s.save(); Privilege[] read = privilegesFromName(Privilege.JCR_READ); try { JackrabbitAccessControlList tmpl = getPolicy(acMgr, "/", testUser.getPrincipal()); tmpl.addEntry(testUser.getPrincipal(), read, false, getRestrictions(superuser, path)); acMgr.setPolicy(tmpl.getPath(), tmpl); superuser.save(); // empty save operation s.save(); } finally { // undo revocation of read privilege JackrabbitAccessControlList tmpl = getPolicy(acMgr, "/", testUser.getPrincipal()); tmpl.addEntry(testUser.getPrincipal(), read, true, getRestrictions(superuser, path)); acMgr.setPolicy(tmpl.getPath(), tmpl); superuser.save(); } } public void testReorderPolicyNode() throws RepositoryException, NotExecutableException { Session testSession = getTestSession(); Node n = testSession.getNode(path); try { if (!n.getPrimaryNodeType().hasOrderableChildNodes()) { throw new NotExecutableException("Reordering child nodes is not supported.."); } n.orderBefore(Text.getName(childNPath), Text.getName(childNPath2)); testSession.save(); fail("test session must not be allowed to reorder nodes."); } catch (AccessDeniedException e) { // success. } // grant all privileges givePrivileges(path, privilegesFromNames(new String[] {Privilege.JCR_ALL}), getRestrictions(superuser, path)); n.orderBefore("rep:policy", Text.getName(childNPath2)); testSession.save(); } }