blob: f232be2f1e3dee02845e19163895ffa7f228d4a4 [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.exercise.security.authorization.accesscontrol;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlException;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import com.google.common.collect.ImmutableList;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.JackrabbitWorkspace;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.apache.jackrabbit.oak.exercise.ExerciseUtility;
import org.apache.jackrabbit.oak.security.authorization.accesscontrol.InvalidTestPrincipal;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
import org.apache.jackrabbit.test.AbstractJCRTest;
import org.apache.jackrabbit.test.NotExecutableException;
import static org.junit.Assert.assertArrayEquals;
/**
* <pre>
* Module: Authorization (Access Control Management)
* =============================================================================
*
* Title: AccessControlList Implementation Details
* -----------------------------------------------------------------------------
*
* Goal:
* Understand some of the implementation details applied by the default
* access control list provided by the Oak access control management.
*
* Exercises:
*
* - {@link #testAddEntryTwice()}
* Adding the same ACE twice does not work.
* Verify the expectation by looking at the ACEs exposed by the list.
*
* - {@link #testUpdateAndComplementary()}
* The default implementation of the JackrabbitAccessControlList interface
* performs some optimization upon ACE-addition.
* Walk through the setup and complete the test case such that it passes.
*
* - {@link #testAddEntryWithInvalidPrincipals()}
* This tests creates a list of invalid principals for which adding an ACE
* will fail.
*
* Question: Can you explain for each of these principals why?
*
* - {@link #testAddEntriesWithCustomKnownPrincipal()}
* Here we use a custom principal implementation as well but a principal
* with the given name is actually known.
* Walk through the test and complete it such that it passes.
*
* - {@link #testAddEntryWithInvalidPrivilege()}
* Walk through the test and explain why creating ACE for the given list of
* privilege arrays must fail.
*
* - {@link #testRemoveInvalidEntry()}
* Walk through the removal and explain why removing an ACE with the same
* characteristics is expected to fail.
*
*
* Additional Exercises:
* -----------------------------------------------------------------------------
*
* The JCR specification mandates the that the principal used to create an ACE
* is known to the system.
*
* - Investigate how the Oak repository can be configured such that creating
* ACEs with unknown principals would still succeed.
*
* Question: Can you name the configuration option and list the allowed values? What are the differences?
* Question: Can you find other places in the access control management code
* base where this is being used?
* Question: Can you imagine the use cases for such a different or relaxed behaviour?
*
*
* Related Exercises:
* -----------------------------------------------------------------------------
*
* - {@link L6_AccessControlContentTest}
*
* </pre>
*/
public class L5_AccessControlListImplTest extends AbstractJCRTest {
private AccessControlManager acMgr;
private JackrabbitAccessControlList acl;
private Principal testPrincipal;
private Privilege[] testPrivileges;
@Override
protected void setUp() throws Exception {
super.setUp();
acMgr = superuser.getAccessControlManager();
testPrincipal = ExerciseUtility.createTestGroup(((JackrabbitSession) superuser).getUserManager()).getPrincipal();
superuser.save();
acl = AccessControlUtils.getAccessControlList(superuser, testRoot);
if (acl == null) {
throw new NotExecutableException();
}
testPrivileges = AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_READ, Privilege.JCR_WRITE);
}
@Override
protected void tearDown() throws Exception {
try {
Authorizable testGroup = ((JackrabbitSession) superuser).getUserManager().getAuthorizable(testPrincipal);
if (testGroup != null) {
testGroup.remove();
superuser.save();
}
} finally {
super.tearDown();
}
}
public void testAddEntryTwice() throws Exception {
acl.addEntry(testPrincipal, testPrivileges, true, Collections.<String, Value>emptyMap());
boolean expectedResult = false; // EXERCISE
assertEquals(expectedResult, acl.addEntry(testPrincipal, testPrivileges, true, Collections.<String, Value>emptyMap()));
// EXERCISE : verify the size of the ACL.
}
public void testUpdateAndComplementary() throws Exception {
Privilege[] readPriv = AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_READ);
Privilege[] writePriv = AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_WRITE);
Privilege[] acReadPriv = AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_READ_ACCESS_CONTROL);
assertTrue(acl.addEntry(testPrincipal, readPriv, true));
assertTrue(acl.addEntry(testPrincipal, writePriv, true));
assertTrue(acl.addEntry(testPrincipal, acReadPriv, true));
int expectedSize = -1; // EXERCISE
assertEquals(expectedSize, acl.size());
assertTrue(acl.addEntry(testPrincipal, readPriv, false));
expectedSize = -1; // EXERCISE
assertEquals(expectedSize, acl.size());
AccessControlEntry[] entries = acl.getAccessControlEntries();
Privilege[] expectedPrivileges = null; // EXERCISE
assertArrayEquals(expectedPrivileges, entries[0].getPrivileges());
Privilege[] expectedPrivileges1 = null; // EXERCISE
assertArrayEquals(expectedPrivileges1, entries[1].getPrivileges());
}
public void testAddEntryWithInvalidPrincipals() throws Exception {
// EXERCISE: explain for each principal in the list why using it for an ACE fails
List<Principal> invalidPrincipals = ImmutableList.of(
new InvalidTestPrincipal("unknown"),
null,
new PrincipalImpl(""), new Principal() {
@Override
public String getName() {
return "unknown";
}
});
for (Principal principal : invalidPrincipals) {
try {
acl.addAccessControlEntry(principal, testPrivileges);
fail("Adding an ACE with an invalid principal should fail");
} catch (AccessControlException e) {
// success
}
}
}
public void testAddEntriesWithCustomKnownPrincipal() throws Exception {
Principal oakPrincipal = new PrincipalImpl(testPrincipal.getName());
Principal principal = new Principal() {
@Override
public String getName() {
return testPrincipal.getName();
}
};
assertTrue(acl.addAccessControlEntry(oakPrincipal, AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_READ)));
assertTrue(acl.addAccessControlEntry(principal, AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_READ_ACCESS_CONTROL)));
int expectedLength = -1; // EXERCISE
assertEquals(expectedLength, acl.getAccessControlEntries().length);
}
public void testAddEntryWithInvalidPrivilege() throws Exception {
String privilegeName = "AccessControlListImplTestPrivilege";
Privilege customPriv = ((JackrabbitWorkspace) superuser.getWorkspace()).getPrivilegeManager().registerPrivilege(privilegeName, true, new String[0]);
// EXERCISE : walks through this test and explain why adding those ACEs fails.
List<Privilege[]> invalidPrivileges = ImmutableList.of(
new Privilege[0],
null,
new Privilege[] {customPriv}
);
for (Privilege[] privs : invalidPrivileges) {
try {
acl.addAccessControlEntry(testPrincipal, privs);
fail("Adding an ACE with invalid privilege array should fail.");
} catch (AccessControlException e) {
// success
}
}
}
public void testRemoveInvalidEntry() throws RepositoryException {
assertTrue(AccessControlUtils.addAccessControlEntry(superuser, testRoot, testPrincipal, testPrivileges, true));
// EXERCISE : walk through the removal and explain the expected behaviour.
try {
acl.removeAccessControlEntry(new JackrabbitAccessControlEntry() {
public boolean isAllow() {
return false;
}
public String[] getRestrictionNames() {
return new String[0];
}
public Value getRestriction(String restrictionName) {
return null;
}
public Value[] getRestrictions(String restrictionName) {
return null;
}
public Principal getPrincipal() {
return testPrincipal;
}
public Privilege[] getPrivileges() {
return testPrivileges;
}
});
fail("Passing an unknown ACE should fail");
} catch (AccessControlException e) {
// success
}
}
}