/*
* (C) Copyright 2006-2011 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:
* Nuxeo - initial API and implementation
*
* $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
*/
package org.nuxeo.ecm.core.api.security;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
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.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.READ_WRITE;
import static org.nuxeo.ecm.core.api.security.SecurityConstants.RESTRICTED_READ;
import static org.nuxeo.ecm.core.api.security.SecurityConstants.WRITE;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.nuxeo.ecm.core.api.security.impl.ACLImpl;
import org.nuxeo.ecm.core.api.security.impl.ACPImpl;
import org.nuxeo.runtime.mockito.MockitoFeature;
import org.nuxeo.runtime.mockito.RuntimeService;
import org.nuxeo.runtime.test.runner.Features;
import org.nuxeo.runtime.test.runner.FeaturesRunner;
import org.nuxeo.runtime.test.runner.RuntimeFeature;
@RunWith(FeaturesRunner.class)
@Features({ RuntimeFeature.class, MockitoFeature.class })
public class TestACP {
@Mock
@RuntimeService
protected AdministratorGroupsProvider administratorGroupsProvider;
private ACP acp;
@Before
public void doBefore() throws Exception {
when(administratorGroupsProvider.getAdministratorsGroups()).thenReturn(
Collections.singletonList("administrators"));
}
@Before
public void setUp() {
acp = new ACPImpl();
}
@After
public void tearDown() {
acp = null;
}
@Test
public void testGetACLs() {
ACL[] acls = acp.getACLs();
assertEquals(0, acls.length);
}
@Test
public void testAddAndRemoveACL() {
ACL acl1 = new ACLImpl("acl1");
ACL acl2 = new ACLImpl("acl2");
acp.addACL(acl1);
assertEquals(1, acp.getACLs().length);
assertEquals(acl1, acp.getACLs()[0]);
acp.addACL(acl2);
acp.removeACL("acl1");
acp.removeACL("acl2");
assertEquals(0, acp.getACLs().length);
// Check that order doesn't matter
acp.addACL(acl1);
acp.addACL(acl2);
acp.removeACL("acl1");
acp.removeACL("acl2");
assertEquals(0, acp.getACLs().length);
acp.addACL(acl2);
acp.addACL(acl1);
acp.removeACL("acl1");
acp.removeACL("acl2");
assertEquals(0, acp.getACLs().length);
assertNull(acp.removeACL("acl1"));
}
@Test
public void testCheckAccess() {
ACL acl1 = new ACLImpl("acl1");
ACE ace1 = new ACE("joe", EVERYTHING, true);
acl1.add(ace1);
acp.addACL(acl1);
assertSame(GRANT, acp.getAccess("joe", READ));
assertSame(UNKNOWN, acp.getAccess("joe", RESTRICTED_READ));
assertSame(UNKNOWN, acp.getAccess("jack", READ));
}
@Test
public void testCheckAccessNullACE() {
ACL acl1 = new ACLImpl("acl1");
acl1.add(new ACE());
acl1.add(new ACE(null, EVERYTHING, true));
acl1.add(new ACE(EVERYONE, null, true));
acp.addACL(acl1);
assertSame(UNKNOWN, acp.getAccess("joe", READ));
assertSame(UNKNOWN, acp.getAccess("joe", RESTRICTED_READ));
assertSame(UNKNOWN, acp.getAccess("jack", READ));
}
@Test
public void testPermissionsAPI() {
ACL acl = new ACLImpl("acl1");
ACE bart = new ACE("bart", EVERYTHING, true);
ACE notbart = new ACE("notbart", EVERYTHING, false);
ACE homer = new ACE("homer", BROWSE, true);
ACE lisa = new ACE("lisa", BROWSE, true);
acl.add(bart);
acl.add(notbart);
acl.add(homer);
acl.add(lisa);
acp.addACL(acl);
Set<String> perms = new HashSet<String>(3);
perms.add(BROWSE);
perms.add(READ);
perms.add(WRITE);
String[] usernames = acp.listUsernamesForAnyPermission(perms);
assertEquals(2, usernames.length);
}
@Test
public void testGetOrCreateAcl() {
// create ACL with name ACL.LOCAL_ACL
ACL createdAcl = acp.getOrCreateACL();
createdAcl.add(new ACE("john", "Sing", true));
createdAcl.add(new ACE("anne", "Joke", false));
// check that the ACP has already been affected by the ACL editing
assertTrue(acp.getAccess("john", "Sing").toBoolean());
assertFalse(acp.getAccess("anne", "Joke").toBoolean());
// check that by fetching the acl again we get the same instance
ACL fetchedAcl = acp.getOrCreateACL();
assertEquals(createdAcl, fetchedAcl);
assertTrue(acp.getAccess("john", "Sing").toBoolean());
assertFalse(acp.getAccess("anne", "Joke").toBoolean());
// check that setting the same ACL again does not clear it
acp.addACL(fetchedAcl);
assertEquals(createdAcl, fetchedAcl);
assertTrue(acp.getAccess("john", "Sing").toBoolean());
assertFalse(acp.getAccess("anne", "Joke").toBoolean());
// check that setting an empty ACL with the same name clear the
// permissions
acp.addACL(new ACLImpl(ACL.LOCAL_ACL));
assertFalse(acp.getAccess("john", "Sing").toBoolean());
}
@Test
public void itCanAddExistingPermission() throws Exception {
// Given an ACP
ACP acp = getInheritedReadWriteACP();
// When i set Permission to a user
acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", READ_WRITE).creator("john").build());
// Then he still have access and local ACL have an entry
assertEquals(Access.GRANT, acp.getAccess("john", READ_WRITE));
assertEquals(1, acp.getACL(ACL.LOCAL_ACL).getACEs().length);
assertEquals(ACE.builder("john", READ_WRITE).creator("john").build(), acp.getACL(ACL.LOCAL_ACL).getACEs()[0]);
}
@Test
public void itCanAddPermission() throws Exception {
// Given an ACP
ACP acp = getInheritedReadWriteACP();
// Make a first call to fill cache
assertEquals(Access.UNKNOWN, acp.getAccess("john", "comment"));
// When i set Permission to a user
acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", "comment").creator("john").build());
// Then he still have access and local ACL have an entry
assertEquals(Access.GRANT, acp.getAccess("john", "comment"));
assertEquals(1, acp.getACL(ACL.LOCAL_ACL).getACEs().length);
assertEquals(ACE.builder("john", "comment").creator("john").build(), acp.getACL(ACL.LOCAL_ACL).getACEs()[0]);
}
@Test
public void itShouldNotAddPermissionTwice() throws Exception {
// Given an ACP
ACP acp = getInheritedReadWriteACP();
// When i set Permission to a user
boolean hasChanged = acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", READ_WRITE).build());
assertTrue(hasChanged);
// When i call the operation another time
hasChanged = acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", READ_WRITE).build());
// Then nothing should change
assertFalse(hasChanged);
assertEquals(1, acp.getACL(ACL.LOCAL_ACL).getACEs().length);
}
@Test
public void itCanBlockInheritance() throws Exception {
// Given an ACP
ACP acp = getInheritedReadWriteACP();
// When i add the permission to a user with blocking inheritance
acp.blockInheritance(ACL.LOCAL_ACL, "john");
acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", READ_WRITE).creator("john").build());
// Then he still have access and local ACL have an entry
assertEquals(Access.GRANT, acp.getAccess("john", READ_WRITE));
// Other user don't have access anymore
assertEquals(Access.DENY, acp.getAccess("jack", READ_WRITE));
// Administrators still have access
assertEquals(Access.GRANT, acp.getAccess("administrators", READ_WRITE));
// unblock the inheritance
acp.unblockInheritance(ACL.LOCAL_ACL);
assertEquals(Access.GRANT, acp.getAccess("john", READ_WRITE));
assertEquals(Access.GRANT, acp.getAccess("jack", READ_WRITE));
}
@Test(expected = NullPointerException.class)
public void blockingInheritanceNeedsACurrentPrincipal() throws Exception {
// Given an ACP
ACP acp = getInheritedReadWriteACP();
// When i add the permission to a user with blocking inheritance
acp.blockInheritance(ACL.LOCAL_ACL, null);
acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", READ_WRITE).build());
}
@Test
public void itShouldAddInheritanceEvenIfItAlreadyHasPermission() throws Exception {
// Given an ACP
ACP acp = getInheritedReadWriteACP();
ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL);
acl.add(new ACE("john", READ_WRITE, true));
// When i add the permission to a user with blocking inheritance
acp.blockInheritance(ACL.LOCAL_ACL, "john");
acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", READ_WRITE).creator("john").build());
// Then he still have access and local ACL have an entry
assertEquals(Access.GRANT, acp.getAccess("john", READ_WRITE));
}
@Test
public void itCanRemovePermissionsToAUser() throws Exception {
// Given an ACP
ACP acp = new ACPImpl();
String jack = "jack";
ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL);
acl.add(new ACE("john", READ_WRITE, true));
acl.add(new ACE(jack, READ_WRITE, true));
acl.add(new ACE(jack, "comment", true));
acl.add(new ACE("jerry", READ_WRITE, true));
assertEquals(Access.GRANT, acp.getAccess("jack", READ_WRITE));
assertEquals(4, acp.getACL(ACL.LOCAL_ACL).getACEs().length);
// When i call the removePermission operation
boolean hasChanged = acp.removeACEsByUsername(ACL.LOCAL_ACL, jack);
// Then the user doesn't have any permission anymore
assertTrue(hasChanged);
assertEquals(Access.UNKNOWN, acp.getAccess("jack", READ_WRITE));
assertEquals(Access.UNKNOWN, acp.getAccess("jack", "comment"));
assertEquals(2, acp.getACL(ACL.LOCAL_ACL).getACEs().length);
}
@Test
public void itDoesNotChangeSecurityWhenRemovingNonExistentUser() throws Exception {
// Given an ACP
ACP acp = new ACPImpl();
assertEquals(Access.UNKNOWN, acp.getAccess("jack", READ_WRITE));
// When i call the removePermission operation
boolean hasChanged = acp.removeACEsByUsername(ACL.LOCAL_ACL, "jack");
// Then the user doesn't have any permission anymore
assertFalse(hasChanged);
assertEquals(Access.UNKNOWN, acp.getAccess("jack", READ_WRITE));
}
@Test
public void itCanRemovePermissionGivenItsId() throws Exception {
// Given an ACP
ACP acp = new ACPImpl();
String jack = "jack";
ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL);
acl.add(new ACE("john", READ_WRITE, true));
acl.add(new ACE(jack, READ_WRITE, true));
acl.add(new ACE(jack, "comment", true));
ACE ace = new ACE("jerry", READ_WRITE, true);
acl.add(ace);
assertEquals(Access.GRANT, acp.getAccess("jack", READ_WRITE));
assertEquals(Access.GRANT, acp.getAccess("jerry", READ_WRITE));
assertEquals(4, acp.getACL(ACL.LOCAL_ACL).getACEs().length);
// When i call the removePermission operation
boolean hasChanged = acp.removeACE(ACL.LOCAL_ACL, ace);
// Then the user doesn't have any permission anymore
assertTrue(hasChanged);
assertEquals(Access.GRANT, acp.getAccess("jack", READ_WRITE));
assertEquals(Access.GRANT, acp.getAccess("jack", "comment"));
assertEquals(Access.UNKNOWN, acp.getAccess("jerry", READ_WRITE));
assertEquals(3, acp.getACL(ACL.LOCAL_ACL).getACEs().length);
}
@Test
public void testMultipleNewPermissionsWithBlockInheritance() {
// Given an ACP
ACP acp = getInheritedReadWriteACP();
// Make a first call to fill cache
assertEquals(Access.UNKNOWN, acp.getAccess("john", "comment"));
// When i set Permission to a user with inheritance block
acp.blockInheritance(ACL.LOCAL_ACL, "john");
acp.addACE(ACL.LOCAL_ACL, ACE.builder("john", READ_WRITE).creator("john").build());
// only john have Permission
assertEquals(Access.GRANT, acp.getAccess("john", "ReadWrite"));
assertEquals(Access.DENY, acp.getAccess("jack", "ReadWrite"));
assertEquals(Access.DENY, acp.getAccess("jerry", "ReadWrite"));
acp.addACE(ACL.LOCAL_ACL, ACE.builder("jack", READ_WRITE).creator("john").build());
acp.addACE(ACL.LOCAL_ACL, ACE.builder("jerry", READ_WRITE).creator("john").build());
// Check jack and jerry have permission, even with inheritance block
assertEquals(Access.GRANT, acp.getAccess("john", "ReadWrite"));
assertEquals(Access.GRANT, acp.getAccess("jack", "ReadWrite"));
assertEquals(Access.GRANT, acp.getAccess("jerry", "ReadWrite"));
}
private ACP getInheritedReadWriteACP() {
ACP acp = new ACPImpl();
ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL);
acl = acp.getOrCreateACL(ACL.INHERITED_ACL);
acl.add(new ACE(SecurityConstants.EVERYONE, SecurityConstants.READ_WRITE, true));
return acp;
}
}