/* * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed 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. * * Contributors: * Florent Guillaume * Benoit Delbosc */ package org.nuxeo.ecm.core; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.nuxeo.ecm.core.api.security.Access.DENY; import static org.nuxeo.ecm.core.api.security.Access.GRANT; import static org.nuxeo.ecm.core.api.security.Access.UNKNOWN; import static org.nuxeo.ecm.core.api.security.SecurityConstants.ADD_CHILDREN; import static org.nuxeo.ecm.core.api.security.SecurityConstants.BROWSE; import static org.nuxeo.ecm.core.api.security.SecurityConstants.EVERYONE; import static org.nuxeo.ecm.core.api.security.SecurityConstants.EVERYTHING; import static org.nuxeo.ecm.core.api.security.SecurityConstants.READ; import static org.nuxeo.ecm.core.api.security.SecurityConstants.REMOVE; import static org.nuxeo.ecm.core.api.security.SecurityConstants.REMOVE_CHILDREN; import static org.nuxeo.ecm.core.api.security.SecurityConstants.WRITE; import static org.nuxeo.ecm.core.api.security.SecurityConstants.WRITE_PROPERTIES; import static org.nuxeo.ecm.core.api.security.SecurityConstants.WRITE_SECURITY; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.inject.Inject; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.Priority; import org.apache.log4j.spi.LoggingEvent; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.nuxeo.ecm.core.api.CoreInstance; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.DocumentRef; import org.nuxeo.ecm.core.api.DocumentSecurityException; import org.nuxeo.ecm.core.api.NuxeoPrincipal; import org.nuxeo.ecm.core.api.PathRef; import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; import org.nuxeo.ecm.core.api.impl.UserPrincipal; import org.nuxeo.ecm.core.api.security.ACE; import org.nuxeo.ecm.core.api.security.ACL; import org.nuxeo.ecm.core.api.security.ACP; import org.nuxeo.ecm.core.api.security.SecurityConstants; import org.nuxeo.ecm.core.api.security.UserEntry; import org.nuxeo.ecm.core.api.security.impl.ACLImpl; import org.nuxeo.ecm.core.api.security.impl.ACPImpl; import org.nuxeo.ecm.core.api.security.impl.UserEntryImpl; import org.nuxeo.ecm.core.security.SecurityService; import org.nuxeo.ecm.core.storage.sql.coremodel.SQLSession; import org.nuxeo.ecm.core.test.CoreFeature; import org.nuxeo.ecm.core.test.annotations.Granularity; import org.nuxeo.ecm.core.test.annotations.RepositoryConfig; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.test.runner.Features; import org.nuxeo.runtime.test.runner.FeaturesRunner; import org.nuxeo.runtime.test.runner.LocalDeploy; import org.nuxeo.runtime.test.runner.LogCaptureFeature; import org.nuxeo.runtime.transaction.TransactionHelper; @RunWith(FeaturesRunner.class) @Features({ CoreFeature.class, LogCaptureFeature.class }) @RepositoryConfig(cleanup = Granularity.METHOD) @LocalDeploy({ "org.nuxeo.ecm.core.test.tests:OSGI-INF/test-repo-core-types-contrib.xml", "org.nuxeo.ecm.core.test.tests:OSGI-INF/test-permissions-contrib.xml" }) public class TestSQLRepositorySecurity { @Inject protected CoreFeature coreFeature; @Inject protected CoreSession session; @Before public void setUp() { if (allowNegativeAcl()) { Framework.getProperties().setProperty(SQLSession.ALLOW_NEGATIVE_ACL_PROPERTY, "true"); } } @After public void tearDown() { Framework.getProperties().remove(SQLSession.ALLOW_NEGATIVE_ACL_PROPERTY); } protected CoreSession openSessionAs(String username) { return CoreInstance.openCoreSession(session.getRepositoryName(), username); } protected CoreSession openSessionAs(NuxeoPrincipal principal) { return CoreInstance.openCoreSession(session.getRepositoryName(), principal); } // overridden to test with allowed negative properties protected boolean allowNegativeAcl() { return false; // default in Nuxeo } // assumes that the global "session" belongs to an Administrator protected void setPermissionToAnonymous(String perm) { DocumentModel doc = session.getRootDocument(); ACP acp = doc.getACP(); if (acp == null) { acp = new ACPImpl(); } UserEntryImpl userEntry = new UserEntryImpl("anonymous"); userEntry.addPrivilege(perm); acp.setRules("test", new UserEntry[] { userEntry }); doc.setACP(acp, true); session.save(); } protected void setPermissionToEveryone(String... perms) { DocumentModel doc = session.getRootDocument(); ACP acp = doc.getACP(); if (acp == null) { acp = new ACPImpl(); } UserEntryImpl userEntry = new UserEntryImpl(EVERYONE); for (String perm : perms) { userEntry.addPrivilege(perm); } acp.setRules("test", new UserEntry[] { userEntry }); doc.setACP(acp, true); session.save(); } protected void removePermissionToAnonymous() { DocumentModel doc = session.getRootDocument(); ACP acp = doc.getACP(); acp.removeACL("test"); doc.setACP(acp, true); session.save(); } protected void removePermissionToEveryone() { DocumentModel doc = session.getRootDocument(); ACP acp = doc.getACP(); acp.removeACL("test"); doc.setACP(acp, true); session.save(); } @Test public void testSecurity() { // temporary set an Everything privileges on the root for anonymous // so that we can create a folder setPermissionToAnonymous(EVERYTHING); try (CoreSession anonSession = openSessionAs("anonymous")) { DocumentModel root = anonSession.getRootDocument(); DocumentModel folder = new DocumentModelImpl(root.getPathAsString(), "folder#1", "Folder"); folder = anonSession.createDocument(folder); ACP acp = folder.getACP(); assertNotNull(acp); // the acp inherited from root is returned acp = new ACPImpl(); ACL acl = new ACLImpl(); acl.add(new ACE("a", "Read", true)); acl.add(new ACE("b", "Write", true)); acp.addACL(acl); folder.setACP(acp, true); acp = folder.getACP(); assertNotNull(acp); assertEquals("a", acp.getACL(ACL.LOCAL_ACL).get(0).getUsername()); assertEquals("b", acp.getACL(ACL.LOCAL_ACL).get(1).getUsername()); assertSame(GRANT, acp.getAccess("a", "Read")); assertSame(UNKNOWN, acp.getAccess("a", "Write")); assertSame(GRANT, acp.getAccess("b", "Write")); assertSame(UNKNOWN, acp.getAccess("b", "Read")); assertSame(UNKNOWN, acp.getAccess("c", "Read")); assertSame(UNKNOWN, acp.getAccess("c", "Write")); // insert a deny Write ACE before the GRANT acp.getACL(ACL.LOCAL_ACL).add(0, new ACE("b", "Write", false)); // store changes folder.setACP(acp, true); // refetch ac acp = folder.getACP(); // check perms now assertSame(GRANT, acp.getAccess("a", "Read")); assertSame(UNKNOWN, acp.getAccess("a", "Write")); assertSame(DENY, acp.getAccess("b", "Write")); assertSame(UNKNOWN, acp.getAccess("b", "Read")); assertSame(UNKNOWN, acp.getAccess("c", "Read")); assertSame(UNKNOWN, acp.getAccess("c", "Write")); // create a child document and grant on it the write for b // remove anonymous Everything privileges on the root // so that it not influence test results removePermissionToAnonymous(); anonSession.save(); // process invalidations try { DocumentModel folder2 = new DocumentModelImpl(folder.getPathAsString(), "folder#2", "Folder"); folder2 = anonSession.createDocument(folder2); fail("privilege is granted but should not be"); } catch (DocumentSecurityException e) { // ok } setPermissionToAnonymous(EVERYTHING); anonSession.save(); // process invalidations root = anonSession.getRootDocument(); // and try again - this time it should work DocumentModel folder2 = new DocumentModelImpl(folder.getPathAsString(), "folder#2", "Folder"); folder2 = anonSession.createDocument(folder2); ACP acp2 = new ACPImpl(); acl = new ACLImpl(); acl.add(new ACE("b", "Write", true)); acp2.addACL(acl); folder2.setACP(acp2, true); acp2 = folder2.getACP(); assertSame(GRANT, acp2.getAccess("a", "Read")); assertSame(UNKNOWN, acp2.getAccess("a", "Write")); assertSame(GRANT, acp2.getAccess("b", "Write")); assertSame(UNKNOWN, acp2.getAccess("b", "Read")); assertSame(UNKNOWN, acp2.getAccess("c", "Read")); assertSame(UNKNOWN, acp2.getAccess("c", "Write")); // remove anonymous Everything privileges on the root // so that it not influence test results removePermissionToAnonymous(); anonSession.save(); // process invalidations setPermissionToEveryone(WRITE, REMOVE, ADD_CHILDREN, REMOVE_CHILDREN, READ); root = anonSession.getRootDocument(); DocumentModel folder3 = new DocumentModelImpl(folder.getPathAsString(), "folder#3", "Folder"); folder3 = anonSession.createDocument(folder3); anonSession.removeDocument(folder3.getRef()); removePermissionToEveryone(); setPermissionToEveryone(REMOVE); anonSession.save(); // process invalidations try { folder3 = new DocumentModelImpl(folder.getPathAsString(), "folder#3", "Folder"); folder3 = anonSession.createDocument(folder3); fail(); } catch (Exception e) { } } } @Test public void testACLEscaping() { // temporary set an Everything privileges on the root for anonymous // so that we can create a folder setPermissionToAnonymous(EVERYTHING); DocumentModel root = session.getRootDocument(); DocumentModel folder = new DocumentModelImpl(root.getPathAsString(), "folder1", "Folder"); folder = session.createDocument(folder); ACP acp = new ACPImpl(); ACL acl = new ACLImpl(); acl.add(new ACE("xyz", "Read", true)); acl.add(new ACE("abc@def<&>/ ", "Read", true)); acl.add(new ACE("caf\u00e9", "Read", true)); acl.add(new ACE("o'hara", "Read", true)); // name to quote acl.add(new ACE("A_x1234_", "Read", true)); // name to quote acp.addACL(acl); folder.setACP(acp, true); // check what we read acp = folder.getACP(); assertNotNull(acp); acl = acp.getACL(ACL.LOCAL_ACL); assertEquals("xyz", acl.get(0).getUsername()); assertEquals("abc@def<&>/ ", acl.get(1).getUsername()); assertEquals("caf\u00e9", acl.get(2).getUsername()); assertEquals("o'hara", acl.get(3).getUsername()); assertEquals("A_x1234_", acl.get(4).getUsername()); } @Test public void testGetParentDocuments() { setPermissionToAnonymous(EVERYTHING); DocumentModel root = session.getRootDocument(); String name = "Workspaces#1"; DocumentModel workspaces = new DocumentModelImpl(root.getPathAsString(), name, "Workspace"); session.createDocument(workspaces); String name2 = "repositoryWorkspace2#"; DocumentModel repositoryWorkspace = new DocumentModelImpl(workspaces.getPathAsString(), name2, "Workspace"); repositoryWorkspace = session.createDocument(repositoryWorkspace); String name3 = "ws#3"; DocumentModel ws1 = new DocumentModelImpl(repositoryWorkspace.getPathAsString(), name3, "Workspace"); ws1 = session.createDocument(ws1); String name4 = "ws#4"; DocumentModel ws2 = new DocumentModelImpl(ws1.getPathAsString(), name4, "Workspace"); session.createDocument(ws2); if (session.isNegativeAclAllowed()) { ACP acp = new ACPImpl(); ACE denyRead = new ACE("test", READ, false); ACL acl = new ACLImpl(); acl.setACEs(new ACE[] { denyRead }); acp.addACL(acl); // TODO this produces a stack trace repositoryWorkspace.setACP(acp, true); ws1.setACP(acp, true); } session.save(); List<DocumentModel> ws2ParentsUnderAdministrator = session.getParentDocuments(ws2.getRef()); assertTrue("list parents for" + ws2.getName() + "under " + session.getPrincipal().getName() + " is not empty:", !ws2ParentsUnderAdministrator.isEmpty()); try (CoreSession testSession = openSessionAs("test")) { List<DocumentModel> ws2ParentsUnderTest = testSession.getParentDocuments(ws2.getRef()); assertTrue("list parents for" + ws2.getName() + "under " + testSession.getPrincipal().getName() + " is empty:", ws2ParentsUnderTest.isEmpty()); } } @Test public void testACPInheritance() throws Exception { DocumentModel root = new DocumentModelImpl("/", "testACPInheritance", "Folder"); root = session.createDocument(root); DocumentModel doc = new DocumentModelImpl("/testACPInheritance", "folder", "Folder"); doc = session.createDocument(doc); ACP rootAcp = root.getACP(); ACL localACL = rootAcp.getOrCreateACL(); localACL.add(new ACE("joe_reader", READ, true)); root.setACP(rootAcp, true); ACP acp = doc.getACP(); localACL = acp.getOrCreateACL(); localACL.add(new ACE("joe_contributor", WRITE, true)); doc.setACP(acp, true); session.save(); doc = session.getDocument(new PathRef("/testACPInheritance/folder")); acp = doc.getACP(); ACL acl = acp.getACL(ACL.INHERITED_ACL); assertEquals("joe_reader", acl.getACEs()[0].getUsername()); // block inheritance acp.getOrCreateACL().add(new ACE(SecurityConstants.EVERYONE, SecurityConstants.EVERYTHING, false)); doc.setACP(acp, true); session.save(); // now the inherited acl should be null doc = session.getDocument(new PathRef("/testACPInheritance/folder")); acp = doc.getACP(); acl = acp.getACL(ACL.INHERITED_ACL); assertNull(acl); } @Test public void testPermissionChecks() throws Throwable { DocumentRef ref = createDocumentModelWithSamplePermissions("docWithPerms"); try (CoreSession joeReaderSession = openSessionAs("joe_reader")) { // reader only has the right to consult the document DocumentModel joeReaderDoc = joeReaderSession.getDocument(ref); try { joeReaderSession.saveDocument(joeReaderDoc); fail("should have raised a security exception"); } catch (DocumentSecurityException e) { } try { joeReaderSession.createDocument(new DocumentModelImpl(joeReaderDoc.getPathAsString(), "child", "File")); fail("should have raised a security exception"); } catch (DocumentSecurityException e) { } try { joeReaderSession.removeDocument(ref); fail("should have raised a security exception"); } catch (DocumentSecurityException e) { } joeReaderSession.save(); } // contributor only has the right to write the properties of // document try (CoreSession joeContributorSession = openSessionAs("joe_contributor")) { DocumentModel joeContributorDoc = joeContributorSession.getDocument(ref); joeContributorSession.saveDocument(joeContributorDoc); DocumentRef childRef = joeContributorSession.createDocument( new DocumentModelImpl(joeContributorDoc.getPathAsString(), "child", "File")).getRef(); joeContributorSession.save(); // joe contributor can copy the newly created doc joeContributorSession.copy(childRef, ref, "child_copy"); // joe contributor cannot move the doc try { joeContributorSession.move(childRef, ref, "child_move"); fail("should have raised a security exception"); } catch (DocumentSecurityException e) { } // joe contributor cannot remove the folder either try { joeContributorSession.removeDocument(ref); fail("should have raised a security exception"); } catch (DocumentSecurityException e) { } joeContributorSession.save(); } // local manager can read, write, create and remove try (CoreSession joeLocalManagerSession = openSessionAs("joe_localmanager")) { DocumentModel joeLocalManagerDoc = joeLocalManagerSession.getDocument(ref); joeLocalManagerSession.saveDocument(joeLocalManagerDoc); DocumentRef childRef = joeLocalManagerSession.createDocument( new DocumentModelImpl(joeLocalManagerDoc.getPathAsString(), "child2", "File")).getRef(); joeLocalManagerSession.save(); // joe local manager can copy the newly created doc joeLocalManagerSession.copy(childRef, ref, "child2_copy"); // joe local manager cannot move the doc joeLocalManagerSession.move(childRef, ref, "child2_move"); joeLocalManagerSession.removeDocument(ref); joeLocalManagerSession.save(); } } protected DocumentRef createDocumentModelWithSamplePermissions(String name) { DocumentModel root = session.getRootDocument(); DocumentModel doc = new DocumentModelImpl(root.getPathAsString(), name, "Folder"); doc = session.createDocument(doc); ACP acp = doc.getACP(); ACL localACL = acp.getOrCreateACL(); localACL.add(new ACE("joe_reader", READ, true)); localACL.add(new ACE("joe_contributor", READ, true)); localACL.add(new ACE("joe_contributor", WRITE_PROPERTIES, true)); localACL.add(new ACE("joe_contributor", ADD_CHILDREN, true)); localACL.add(new ACE("joe_localmanager", READ, true)); localACL.add(new ACE("joe_localmanager", WRITE, true)); localACL.add(new ACE("joe_localmanager", WRITE_SECURITY, true)); acp.addACL(localACL); doc.setACP(acp, true); // add the permission to remove children on the root ACP rootACP = root.getACP(); ACL rootACL = rootACP.getOrCreateACL(); rootACL.add(new ACE("joe_localmanager", REMOVE_CHILDREN, true)); rootACP.addACL(rootACL); root.setACP(rootACP, true); // make it visible for others session.save(); return doc.getRef(); } @Test @Ignore public void testGetAvailableSecurityPermissions() { List<String> permissions = session.getAvailableSecurityPermissions(); // TODO assertTrue(permissions.contains("Everything")); } @Test public void testReadAclSecurity() { // Check that all permissions that contain Browse enable to list a // document using aclOptimization SecurityService securityService = NXCore.getSecurityService(); String[] browsePermissions = securityService.getPermissionsToCheck(BROWSE); // Check for test permission contribution assertTrue(Arrays.asList(browsePermissions).contains("ViewTest")); List<String> docNames = new ArrayList<String>(browsePermissions.length); DocumentModel root = session.getRootDocument(); for (String permission : browsePermissions) { // Create a folder with only the browse permission String name = "joe-has-" + permission + "-permission"; docNames.add(name); DocumentModel folder = new DocumentModelImpl(root.getPathAsString(), name, "Folder"); folder = session.createDocument(folder); ACP acp = folder.getACP(); assertNotNull(acp); // the acp inherited from root is returned acp = new ACPImpl(); ACL acl = new ACLImpl(); acl.add(new ACE("joe", permission, true)); acp.addACL(acl); folder.setACP(acp, true); } session.save(); try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list; list = joeSession.query("SELECT * FROM Folder"); List<String> names = new ArrayList<String>(); for (DocumentModel doc : list) { names.add(doc.getName()); } assertEquals("Expecting " + docNames + " got " + names, browsePermissions.length, list.size()); list = joeSession.query("SELECT * FROM Folder WHERE ecm:isProxy = 0"); names.clear(); for (DocumentModel doc : list) { names.add(doc.getName()); } assertEquals("Expecting " + docNames + " got " + names, browsePermissions.length, list.size()); // Add a new folder to update the read acls DocumentModel folder = new DocumentModelImpl(root.getPathAsString(), "new-folder", "Folder"); folder = session.createDocument(folder); ACP acp = folder.getACP(); assertNotNull(acp); // the acp inherited from root is returned acp = new ACPImpl(); ACL acl = new ACLImpl(); acl.add(new ACE("joe", browsePermissions[0], true)); acl.add(new ACE("bob", browsePermissions[0], true)); acp.addACL(acl); folder.setACP(acp, true); session.save(); list = joeSession.query("SELECT * FROM Folder"); assertEquals(browsePermissions.length + 1, list.size()); } } @Test public void testReadAclSecurityUpdate() { // check that aclOptimization update the user aclr cache // NXP-13109 DocumentModel root = session.getRootDocument(); // Create a doc and set a new ACLR on it DocumentModel doc = new DocumentModelImpl(root.getPathAsString(), "foo", "Folder"); doc = session.createDocument(doc); ACP acp = doc.getACP(); assertNotNull(acp); acp = new ACPImpl(); ACL acl = new ACLImpl(); acl.add(new ACE("Everyone", "Read", true)); acp.addACL(acl); doc.setACP(acp, true); session.save(); try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list; list = joeSession.query("SELECT * FROM Folder"); assertEquals(1, list.size()); // Remove the document, so the ACLR created is not anymore assigned session.removeDocument(doc.getRef()); session.save(); list = joeSession.query("SELECT * FROM Folder"); assertEquals(0, list.size()); } try (CoreSession bobSession = openSessionAs("bob")) { DocumentModelList list; // Perform a query to init the ACLR cache list = bobSession.query("SELECT * FROM Folder"); assertEquals(0, list.size()); // Create a new doc with the same ACLR doc = new DocumentModelImpl(root.getPathAsString(), "bar", "Folder"); doc = session.createDocument(doc); doc.setACP(acp, true); session.save(); // Check that the ACLR has been added to the user cache list = bobSession.query("SELECT * FROM Folder"); assertEquals(1, list.size()); } } @Test public void testReadAclAfterSetACP() { DocumentModel folder = new DocumentModelImpl("/", "folder", "Folder"); folder = session.createDocument(folder); DocumentModel doc = new DocumentModelImpl("/folder", "doc", "File"); doc = session.createDocument(doc); session.save(); // nothing can be seen by joe try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM Folder"); assertEquals(0, list.size()); list = joeSession.query("SELECT * FROM File"); assertEquals(0, list.size()); } // set ACL on folder ACL acl = new ACLImpl(); acl.add(new ACE("Everyone", "Read", true)); ACP acp = new ACPImpl(); acp.addACL(acl); folder.setACP(acp, true); session.save(); // now joe sees things try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM Folder"); assertEquals(1, list.size()); assertEquals(folder.getId(), list.get(0).getId()); list = joeSession.query("SELECT * FROM File"); assertEquals(1, list.size()); assertEquals(doc.getId(), list.get(0).getId()); } } @Test public void testReadAclAfterCreate() { DocumentModel folder = new DocumentModelImpl("/", "folder", "Folder"); folder = session.createDocument(folder); session.save(); // folder can be seen by a member UserPrincipal joeMember = new UserPrincipal("joe", Arrays.asList("Everyone", "members"), false, false); try (CoreSession joeSession = openSessionAs(joeMember)) { DocumentModelList list = joeSession.query("SELECT * FROM Folder"); assertEquals(1, list.size()); assertEquals(folder.getId(), list.get(0).getId()); } // but not as a non-member try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM Folder"); assertEquals(0, list.size()); } // set ACL on folder ACL acl = new ACLImpl(); acl.add(new ACE("Everyone", "Read", true)); ACP acp = new ACPImpl(); acp.addACL(acl); folder.setACP(acp, true); session.save(); // the folder can be seen by joe try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM Folder"); assertEquals(1, list.size()); assertEquals(folder.getId(), list.get(0).getId()); } // create a doc under the folder DocumentModel doc = new DocumentModelImpl("/folder", "doc", "File"); doc = session.createDocument(doc); session.save(); // the doc can be seen by joe try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM File"); assertEquals(1, list.size()); assertEquals(doc.getId(), list.get(0).getId()); } } @Test public void testReadAclAfterMove() { DocumentModel folder1 = new DocumentModelImpl("/", "folder1", "Folder"); folder1 = session.createDocument(folder1); DocumentModel folder2 = new DocumentModelImpl("/", "folder2", "Folder"); folder2 = session.createDocument(folder2); DocumentModel doc = new DocumentModelImpl("/folder1", "doc", "File"); doc = session.createDocument(doc); // set ACL on folder2 ACL acl = new ACLImpl(); acl.add(new ACE("Everyone", "Read", true)); ACP acp = new ACPImpl(); acp.addACL(acl); folder2.setACP(acp, true); session.save(); // doc under folder1 cannot be read by joe try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM File"); assertEquals(0, list.size()); } // move doc under folder2 session.move(doc.getRef(), folder2.getRef(), null); session.save(); // check doc now readable by joe try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM File"); assertEquals(1, list.size()); } } @Test public void testReadAclAfterCopy() { DocumentModel folder1 = new DocumentModelImpl("/", "folder1", "Folder"); folder1 = session.createDocument(folder1); DocumentModel folder2 = new DocumentModelImpl("/", "folder2", "Folder"); folder2 = session.createDocument(folder2); DocumentModel doc = new DocumentModelImpl("/folder1", "doc", "File"); doc = session.createDocument(doc); // set ACL on folder2 ACL acl = new ACLImpl(); acl.add(new ACE("Everyone", "Read", true)); ACP acp = new ACPImpl(); acp.addACL(acl); folder2.setACP(acp, true); session.save(); // doc under folder1 cannot be read by joe try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM File"); assertEquals(0, list.size()); } // copy doc under folder2 session.copy(doc.getRef(), folder2.getRef(), "doccopy"); session.save(); // check doc copy now readable by joe try (CoreSession joeSession = openSessionAs("joe")) { DocumentModelList list = joeSession.query("SELECT * FROM File"); assertEquals(1, list.size()); assertEquals("doccopy", list.get(0).getName()); } } @Test public void testEmptyLocalACL() throws Exception { DocumentModel doc = session.createDocumentModel("/", "folder", "Folder"); doc = session.createDocument(doc); ACP acp = doc.getACP(); ACL acl = acp.getOrCreateACL(); // don't add anything doc.setACP(acp, true); session.save(); TransactionHelper.commitOrRollbackTransaction(); TransactionHelper.startTransaction(); session = coreFeature.reopenCoreSession(); doc = session.getDocument(doc.getRef()); acp = doc.getACP(); acl = acp.getACL(ACL.LOCAL_ACL); assertNull(acl); } public static class LogDuplicateFilter implements LogCaptureFeature.Filter { @Override public boolean accept(LoggingEvent event) { Level level = event.getLevel(); if (! level.WARN.equals(level)) { return false; } Object msg = event.getMessage(); if (!(msg instanceof String)) { return false; } if (((String) msg).contains("duplicate entry")) { return true; } return false; } } @Inject LogCaptureFeature.Result logCaptureResults; private Priority consoleThresold; protected void hideWarnFromConsole() { Logger rootLogger = Logger.getRootLogger(); ConsoleAppender consoleAppender = (ConsoleAppender) rootLogger.getAppender("CONSOLE"); consoleThresold = consoleAppender.getThreshold(); consoleAppender.setThreshold(Level.ERROR); } protected void restoreConsoleLog() { if (consoleThresold == null) { return; } Logger rootLogger = Logger.getRootLogger(); ConsoleAppender consoleAppender = (ConsoleAppender) rootLogger.getAppender("CONSOLE"); consoleAppender.setThreshold(consoleThresold); consoleThresold = null; } @Test @LogCaptureFeature.FilterWith(value = LogDuplicateFilter.class) public void shouldRemoveDuplicateACE() throws Exception { // Using helper prevent adding duplicates ACL acl = new ACLImpl(); ACE ace = new ACE("leela", "Read"); ACE ace2 = new ACE("bob", "Read"); ACE acedup = new ACE("bob", "Read"); assertTrue(acl.add(ace)); assertTrue(acl.add(ace2)); assertFalse(acl.add(ace)); assertFalse(acl.add(acedup)); assertEquals(2, acl.size()); // Using setACEs at your own risk ACE[] aces = {ace, ace2, ace, acedup}; hideWarnFromConsole(); try { acl.setACEs(aces); } finally { restoreConsoleLog(); } assertEquals(4, acl.size()); // at least we have a warning about duplicate logCaptureResults.assertHasEvent(); } }