You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2015/06/18 16:30:17 UTC

svn commit: r1686235 [3/6] - in /jackrabbit/oak/trunk: ./ oak-doc/src/site/markdown/ oak-exercise/ oak-exercise/src/ oak-exercise/src/main/ oak-exercise/src/main/java/ oak-exercise/src/main/java/org/ oak-exercise/src/main/java/org/apache/ oak-exercise/...

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L4_EffectivePoliciesTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L4_EffectivePoliciesTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L4_EffectivePoliciesTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L4_EffectivePoliciesTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,310 @@
+/*
+ * 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.security.authorization.accesscontrol;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.Privilege;
+
+import com.google.common.collect.ImmutableList;
+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.User;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+
+/**
+ * <pre>
+ * Module: Authorization (Access Control Management)
+ * =============================================================================
+ *
+ * Title: Effective Policies
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Undestand the meaning and nature of retrieving the effective policies for
+ * a given path or set of principals.
+ *
+ * Exercises:
+ *
+ * - {@link #testGetEffectivePolicies()}
+ *   This test create policies at the test root and its child node.
+ *   Fix the test such that the expected number of effective policies is correct.
+ *
+ * - {@link #testGetEffectivePoliciesAtNodeTypeRoot()}
+ *   Implementation specific test retrieve the effective policies for the
+ *   node type root node. Fix the test such that it passes.
+ *
+ *   Question: What is the expected result?
+ *   Question: If there are effective policies, can you explain why?
+ *   Question: Can you also describe the nature of the effective policies?
+ *
+ * - {@link #testGetEffectivePoliciesNewPolicy()}
+ *   Test case illustrating the nature of the effective policies.
+ *   Fix the case such that the assertion is correct.
+ *
+ * - {@link #testGetEffectivePoliciesByPrincipal()}
+ *   Test case illustrating the usage of
+ *   {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlManager#getEffectivePolicies(java.util.Set)}
+ *   Fill in the expected number of effective policies and explain your expectations.
+ *
+ *
+ * Additional Exercises
+ * -----------------------------------------------------------------------------
+ *
+ * The following exercises use a test session with limited access rights to
+ * retrieve the effective policies.
+ *
+ * - {@link #testSessionGetEffectivePolicies()}
+ *   In this case the effective policies are retrieved with a test session that
+ *   has limited access. Insert the expected number of effective policies and
+ *   explain the result.
+ *
+ * - {@link #testSessionGetEffectivePoliciesWithoutPrivilege()}
+ *   Again the test session with limited access rights is used to retrieve the
+ *   effective policies. Fix the test-case and explain the results based on
+ *   the implementation you can find in {@link org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlManagerImpl}.
+ *
+ * - {@link #testSessionGetEffectivePoliciesByPrincipal()}
+ *   The test session with limited access is used to retrieve effective policies
+ *   by principal. Fix the test case and explain the expected result.
+ *
+ * - {@link #testSessionGetEffectivePoliciesByPrincipalWithoutPrivileges()}
+ *   The same test case again but the test session is not granted jcr:readAccessControl
+ *   privilege. Complete the test-case and explain the result.
+ *
+ * - For these additional tests:
+ *   Compare the results with what is exposed when using an admin session with
+ *   full access everywhere.
+ *
+ *   Question: What are the implications for usage/usability of effective policies in a productive environment?
+ *
+ *
+ * Advanced Exercise
+ * -----------------------------------------------------------------------------
+ *
+ * The JCR specification declares the methods to retrieve effective policies as
+ * 'besteffort'. Discuss the meaning of this and try to imagine implementations
+ * where fullfilling this (vague) API contract might not be feasible or not
+ * even be sensible.
+ *
+ * </pre>
+ *
+ * @see javax.jcr.security.AccessControlManager#getEffectivePolicies(String)
+ * @see org.apache.jackrabbit.api.security.JackrabbitAccessControlManager#getEffectivePolicies(java.util.Set)
+ */
+public class L4_EffectivePoliciesTest extends AbstractJCRTest {
+
+    private String childPath;
+
+    private JackrabbitAccessControlManager acMgr;
+    private JackrabbitAccessControlList acl;
+
+    private User testUser;
+    private Principal testPrincipal;
+    private Privilege[] testPrivileges;
+
+    private Session testSession;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Node child = testRootNode.addNode(nodeName1);
+        childPath = child.getPath();
+
+        testUser = ExerciseUtility.createTestUser(((JackrabbitSession) superuser).getUserManager());
+        testPrincipal = testUser.getPrincipal();
+        superuser.save();
+
+        acMgr = (JackrabbitAccessControlManager) superuser.getAccessControlManager();
+        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 {
+            if (testSession != null && testSession.isLive()) {
+                testSession.logout();
+            }
+            if (testUser != null) {
+                testUser.remove();
+                superuser.save();
+            }
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    private JackrabbitAccessControlList setupPolicy(String path, Privilege[] privileges, Principal principal) throws RepositoryException, NotExecutableException {
+        JackrabbitAccessControlList policy = AccessControlUtils.getAccessControlList(acMgr, path);
+        if (policy != null) {
+            policy.addEntry(principal, privileges, true);
+            acMgr.setPolicy(path, policy);
+        } else {
+            throw new NotExecutableException();
+        }
+        return policy;
+    }
+
+    private Session getTestSession() throws RepositoryException {
+        return superuser.getRepository().login(ExerciseUtility.getTestCredentials(testUser.getID()));
+    }
+
+    public void testGetEffectivePolicies() throws Exception {
+        AccessControlPolicy[] policies = acMgr.getEffectivePolicies(testRoot);
+        int expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, policies.length);
+
+        setupPolicy(testRoot, testPrivileges, testPrincipal);
+        superuser.save();
+
+        policies = acMgr.getEffectivePolicies(testRoot);
+        expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, policies.length);
+
+        policies = acMgr.getEffectivePolicies(childPath);
+        expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, policies.length);
+
+        setupPolicy(childPath, testPrivileges, testPrincipal);
+        superuser.save();
+
+        policies = acMgr.getEffectivePolicies(childPath);
+        expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, policies.length);
+    }
+
+    public void testGetEffectivePoliciesAtNodeTypeRoot() throws Exception {
+        AccessControlPolicy[] policies = acMgr.getEffectivePolicies(NodeTypeConstants.NODE_TYPES_PATH);
+
+        int expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, policies.length);
+
+        // EXERCISE : if there are effective policies at this path, what type of policies to do you expect
+        // EXERCISE : verify your expectation with an assertion
+    }
+
+    public void testGetEffectivePoliciesNewPolicy() throws Exception {
+        setupPolicy(testRoot, testPrivileges, testPrincipal);
+
+        // EXERCISE fix the test such that the assert below passes. explain why this is needed.
+
+        AccessControlPolicy[] policies = acMgr.getEffectivePolicies(testRoot);
+        assertEquals(1, policies.length);
+    }
+
+    public void testGetEffectivePoliciesByPrincipal() throws Exception {
+
+        Set<Principal> principalSet = Collections.singleton(testPrincipal);
+        AccessControlPolicy[] policies = acMgr.getEffectivePolicies(principalSet);
+
+        int expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, policies.length);
+
+        setupPolicy(testRoot, testPrivileges, testPrincipal);
+        setupPolicy(childPath, testPrivileges, testPrincipal);
+
+        expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, acMgr.getEffectivePolicies(principalSet).length);
+
+        superuser.save();
+
+        expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, acMgr.getEffectivePolicies(principalSet).length);
+    }
+
+    public void testSessionGetEffectivePolicies() throws Exception {
+        // grant 'testUser' READ + WRITE privileges at the test root
+        setupPolicy(testRoot, testPrivileges, testPrincipal);
+
+        // grant 'testUser' READ + READ_AC privileges at child path
+        Privilege[] privileges = AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_READ, Privilege.JCR_READ_ACCESS_CONTROL);
+        setupPolicy(childPath, privileges, testPrincipal);
+        superuser.save();
+
+        testSession = getTestSession();
+        AccessControlManager testAcMgr = testSession.getAccessControlManager();
+
+        AccessControlPolicy[] effective = testAcMgr.getEffectivePolicies(childPath);
+        int expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, effective.length);
+    }
+
+    public void testSessionGetEffectivePoliciesWithoutPrivilege() throws Exception {
+        // grant 'testUser' READ + WRITE privileges at the test path
+        setupPolicy(testRoot, testPrivileges, testPrincipal);
+        superuser.save();
+
+        testSession = getTestSession();
+        AccessControlManager testAcMgr = testSession.getAccessControlManager();
+
+        List<String> paths = ImmutableList.of(testRoot, NodeTypeConstants.NODE_TYPES_PATH);
+        for (String path : paths) {
+            // EXERCISE : complete or fix the test case
+            AccessControlPolicy[] effectivePolicies = testAcMgr.getEffectivePolicies(path);
+        }
+    }
+
+    public void testSessionGetEffectivePoliciesByPrincipal() throws Exception {
+        Privilege[] privileges = AccessControlUtils.privilegesFromNames(acMgr, Privilege.JCR_READ, Privilege.JCR_READ_ACCESS_CONTROL);
+        setupPolicy(testRoot, privileges, testPrincipal);
+        setupPolicy(childPath, testPrivileges, EveryonePrincipal.getInstance());
+        superuser.save();
+
+        testSession = getTestSession();
+        JackrabbitAccessControlManager testAcMgr = (JackrabbitAccessControlManager) testSession.getAccessControlManager();
+
+        AccessControlPolicy[] effective = testAcMgr.getEffectivePolicies(Collections.singleton(testPrincipal));
+        int expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, effective.length);
+
+        // EXERCISE : explain the result
+    }
+
+    public void testSessionGetEffectivePoliciesByPrincipalWithoutPrivileges() throws Exception {
+        setupPolicy(testRoot, testPrivileges, testPrincipal);
+        setupPolicy(childPath, testPrivileges, EveryonePrincipal.getInstance());
+        superuser.save();
+
+        testSession = getTestSession();
+        JackrabbitAccessControlManager testAcMgr = (JackrabbitAccessControlManager) testSession.getAccessControlManager();
+
+        AccessControlPolicy[] effective = testAcMgr.getEffectivePolicies(Collections.singleton(testPrincipal));
+        int expectedLength = -1; // EXERCISE
+        assertEquals(expectedLength, effective.length);
+
+        // EXERCISE : explain the result
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L4_EffectivePoliciesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L5_AccessControlListImplTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L5_AccessControlListImplTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L5_AccessControlListImplTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L5_AccessControlListImplTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,276 @@
+/*
+ * 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.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.security.ExerciseUtility;
+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
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L5_AccessControlListImplTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L6_AccessControlContentTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L6_AccessControlContentTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L6_AccessControlContentTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L6_AccessControlContentTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,229 @@
+/*
+ * 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.security.authorization.accesscontrol;
+
+import java.security.Principal;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.security.AccessControlList;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.api.JackrabbitSession;
+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.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+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: Representation of Access Control Content in the Repository
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Understand how the default implementation represents access control content
+ * in the repository.
+ *
+ * Exercises:
+ *
+ * - Overview
+ *   Look {@code org/apache/jackrabbit/oak/plugins/nodetype/write/builtin_nodetypes.cnd}
+ *   and try to identify the built in node types used to store access control
+ *   content.
+ *
+ *   Question: Can explain the meaning of all types?
+ *   Question: Why are most item definitions protected?
+ *   Question: Can you identify node types that are not used? Can you explain why?
+ *
+ * - {@link #testAclContent()}
+ *   This test case writes an ACL to the repository.
+ *   Fix the test such that it retrieves the policy node and verify the expected
+ *   nature of this node.
+ *
+ * - {@link #testAceContent()}
+ *   This test case writes an ACL with entries to the repository.
+ *   Fix the test such that it retrieves the policy node and verify the expected
+ *   nature of the access control entry nodes.
+ *
+ * - {@link #testRestrictionContent()}
+ *   Same as above but this time you should look at the restrictions and how they
+ *   are represented in the content.
+ *
+ * - {@link #testMixins()}
+ *   Fix the test by defining the expected mixin types.
+ *
+ *   Question: Can you explain why those mixins are present and who added them?
+ *   Question: Can make a recommendation for other developers wrt the ac-related mixin types? Should they be added manually?
+ *
+ * - {@link #testRepoPolicy()}
+ *   Same as {@link #testAclContent()} but this time for the 'null' path.
+ *   Fix the test case and verify your expections.
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - Named {@code ReadPolicy}
+ *   In the previous exercises you learned about the special named policy
+ *   {@link org.apache.jackrabbit.oak.security.authorization.accesscontrol.AccessControlManagerImpl.ReadPolicy}.
+ *
+ *   Question: Can you find the content representation of this policy?
+ *   Question: Can you explain what is happening?
+ *
+ * </pre>
+ */
+public class L6_AccessControlContentTest 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 testAclContent() throws RepositoryException {
+        acMgr.setPolicy(testRoot, acl);
+
+        // EXERCISE retrieve the policy node and verify the expected name, primary type and child items
+        String policyPath = null;
+        Node aclNode = superuser.getNode(policyPath);
+
+        String expectedName = null;
+        assertEquals(expectedName, aclNode.getName());
+
+        String expectedPrimaryTypeName = null;
+        assertEquals(expectedPrimaryTypeName, aclNode.getPrimaryNodeType().getName());
+
+        NodeIterator aclChildren = aclNode.getNodes();
+        // EXERCISE verify the correct number + expected nature of the children.
+    }
+
+    public void testAceContent() throws RepositoryException {
+        acl.addAccessControlEntry(testPrincipal, testPrivileges);
+        acl.addEntry(EveryonePrincipal.getInstance(), testPrivileges, false);
+        acMgr.setPolicy(testRoot, acl);
+
+        String policyPath = null; // EXERCISE
+        Node aclNode = superuser.getNode(policyPath);
+
+        NodeIterator aclChildren = aclNode.getNodes();
+
+        int expectedSize = -1; // EXERCISE
+        assertEquals(expectedSize, aclChildren.getSize());
+
+        String expectedPrimaryTypeName = null; // EXERCISE: define the type of the first child node.
+        while (aclChildren.hasNext()) {
+            Node ace = aclChildren.nextNode();
+
+            assertEquals(expectedPrimaryTypeName, ace.getPrimaryNodeType().getName());
+            expectedPrimaryTypeName = null; // EXERCISE: define the type of the next item.
+        }
+
+        Node ace = aclNode.getNodes().nextNode();
+        // EXERCISE: retrieve all mandatory ac-related properties of this node and verify the expected value.
+    }
+
+    public void testRestrictionContent() throws RepositoryException {
+        ValueFactory vf = superuser.getValueFactory();
+        acl.addEntry(testPrincipal, testPrivileges, false,
+                ImmutableMap.of(AccessControlConstants.REP_GLOB, vf.createValue("")),
+                ImmutableMap.of(AccessControlConstants.REP_PREFIXES, new Value[] {vf.createValue("jcr"), vf.createValue("mix")}));
+        acMgr.setPolicy(testRoot, acl);
+
+        String policyPath = null; // EXERCISE
+        Node aclNode = superuser.getNode(policyPath);
+
+        Node ace = aclNode.getNodes().nextNode();
+        // EXERCISE: retrieve the restrictions defined for the single ACE node
+        // EXERCISE: verify the expected properties and their value(s)
+    }
+
+    public void testMixins() throws RepositoryException {
+        acMgr.setPolicy(testRoot, acl);
+
+        NodeType[] mixins = superuser.getNode(acl.getPath()).getMixinNodeTypes();
+        NodeType[] expectedMixins = null;
+        assertArrayEquals(expectedMixins, mixins);
+    }
+
+    public void testRepoPolicy() throws RepositoryException {
+        AccessControlList repoAcl = AccessControlUtils.getAccessControlList(acMgr, null);
+
+        assertNotNull(repoAcl);
+        repoAcl.addAccessControlEntry(testPrincipal, AccessControlUtils.privilegesFromNames(acMgr, PrivilegeConstants.JCR_NAMESPACE_MANAGEMENT));
+        acMgr.setPolicy(null, repoAcl);
+
+        // EXERCISE retrieve the policy node and verify the expected name, primary type and child items
+        String policyPath = null;
+        Node aclNode = superuser.getNode(policyPath);
+
+        String expectedName = null;
+        assertEquals(expectedName, aclNode.getName());
+
+        String expectedPrimaryTypeName = null;
+        assertEquals(expectedPrimaryTypeName, aclNode.getPrimaryNodeType().getName());
+
+        NodeIterator aclChildren = aclNode.getNodes();
+        // EXERCISE verify the correct number + expected nature of the children.
+
+        // EXERCISE: can you also identify which mixins are being involved and where they got applied?
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L6_AccessControlContentTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L7_RestrictionsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L7_RestrictionsTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L7_RestrictionsTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L7_RestrictionsTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,197 @@
+/*
+ * 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.security.authorization.accesscontrol;
+
+import java.security.Principal;
+import java.util.Map;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.security.AccessControlEntry;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.Privilege;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.api.JackrabbitSession;
+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.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.AccessControlConstants;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+
+/**
+ * <pre>
+ * Module: Authorization (Access Control Management)
+ * =============================================================================
+ *
+ * Title: Restrictions and Restriction Management
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Become familiar with the concept of additional restrictions added to a given
+ * access control entry and the API to read and write them.
+ *
+ * For simplicity this test make use of the default restrictions provided by Oak.
+ * Be aware that these are not built-in constants as additional restrictions
+ * are pluggable at runtime. See the advanced exercises.
+ *
+ * Exercises:
+ *
+ * - {@link #testApplicableRestrictions()}
+ *   This test uses Jackrabbit API methods to obtain the applicable restrictions.
+ *   Complete the test such that you also now the required type of the
+ *   restrictions.
+ *
+ *   Question: Can you determine from the Jackrabbit API if the restriction is multivalued?
+ *
+ * - {@link #testAddEntryWithRestrictions()}
+ *   Create an new ACE with a single valued restriction like e.g. the path globbing
+ *   restriction.
+ *
+ * - {@link #testAddEntryWithMultipleRestrictions()}
+ *   Create an new ACE with multiple restrictions mixing both single and multi-
+ *   valued restrictions.
+ *
+ * - {@link #testRetrieveRestrictionsFromACE()}
+ *   This test creates an ACE with restrictions. Complete the test by verifying
+ *   your expectations wrt restrictions present on the ACE.
+ *
+ *
+ * Advanced Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * While the restriction API provided by Jackrabbit API is rather limited the
+ * Oak internal way to handle, store and read these restictions is a bit
+ * more elaborate.
+ *
+ * Use the Oak code base and the documentation at
+ * http://jackrabbit.apache.org/oak/docs/security/accesscontrol/restriction.html
+ * to complete the following additional exercises.
+ *
+ * - Take a look at the interfaces and classes defined in
+ *   {@code org.apache.jackrabbit.oak.spi.security.authorization.restriction}
+ *
+ * - Investigate how you could plug your custom restriction provider and try
+ *   to implement it according to the instructions on the Oak.
+ *   Use the stub at {@link org.apache.jackrabbit.oak.security.authorization.restriction.CustomRestrictionProvider}
+ *   to complete this exercise.
+ *
+ * - Make your custom restriction provider an OSGi service and deploy it in a
+ *   OSGi-base repository setup like Sling (Granite|CQ). Use a low-level
+ *   repository browser tool (or a test) to create ACEs making use of the custom
+ *   restrictions you decided to implement.
+ *
+ * </pre>
+ *
+ * @see <a href="http://jackrabbit.apache.org/oak/docs/security/accesscontrol/restriction.html">Restriction Management Documentation</a>
+ */
+public class L7_RestrictionsTest 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 testApplicableRestrictions() throws RepositoryException {
+        String[] restrictionNames = acl.getRestrictionNames();
+
+        for (String name : restrictionNames) {
+            int type = acl.getRestrictionType(name);
+            int expectedType = PropertyType.UNDEFINED;
+
+            if (AccessControlConstants.REP_GLOB.equals(name)) {
+                expectedType = PropertyType.UNDEFINED; // EXERCISE
+            } else if (AccessControlConstants.REP_NT_NAMES.equals(name)) {
+                expectedType = PropertyType.UNDEFINED; // EXERCISE
+            } else if (AccessControlConstants.REP_PREFIXES.equals(name)) {
+                expectedType = PropertyType.UNDEFINED; // EXERCISE
+            }
+            assertEquals(expectedType, type);
+        }
+    }
+
+    public void testAddEntryWithRestrictions() throws RepositoryException {
+        // EXERCISE : create the restriction map containing a globbing pattern.
+        Map<String, Value> restrictions = null;
+
+        assertTrue(acl.addEntry(testPrincipal, testPrivileges, false, restrictions));
+    }
+
+    public void testAddEntryWithMultiValuedRestriction() throws RepositoryException {
+        // EXERCISE : create the restriction map containing a globbing pattern.
+        Map<String, Value> restrictions = null;
+
+        // EXERCISE : create a map with the multi-valued restrictions as well.
+        Map<String, Value[]> mvRestrictions = null;
+
+        assertTrue(acl.addEntry(testPrincipal, testPrivileges, false, restrictions, mvRestrictions));
+    }
+
+    public void testRetrieveRestrictionsFromACE() throws RepositoryException {
+        ValueFactory vf = superuser.getValueFactory();
+
+        acl.addEntry(testPrincipal, testPrivileges, false,
+                ImmutableMap.of(AccessControlConstants.REP_GLOB, vf.createValue("/*")),
+                ImmutableMap.of(AccessControlConstants.REP_PREFIXES, new Value[] {vf.createValue("jcr"), vf.createValue("rep")})
+        );
+
+        for (AccessControlEntry ace : acl.getAccessControlEntries()) {
+            if (ace instanceof JackrabbitAccessControlEntry) {
+                JackrabbitAccessControlEntry jace = (JackrabbitAccessControlEntry) ace;
+
+                // EXERCISE retrieve the restriction names present on the ace and verify your expectations.
+                // EXERCISE retrieve the restriction values for each restriction and verify your expectations.
+            }
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L7_RestrictionsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L8_GlobRestrictionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L8_GlobRestrictionTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L8_GlobRestrictionTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L8_GlobRestrictionTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,51 @@
+/*
+ * 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.security.authorization.accesscontrol;
+
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+
+/**
+ * <pre>
+ * Module: Authorization (Access Control Management)
+ * =============================================================================
+ *
+ * Title: The Globbing Restriction
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * After having completed this exercises you should be familiar with the rep:glob
+ * restriction as present in the default implementation and able to use it to
+ * limit the effect of a given ACE to a given subtree.
+ *
+ * Exercises:
+ *
+ * - {@link #TODO}
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * TODO
+ *
+ * </pre>
+ *
+ * @see org.apache.jackrabbit.oak.security.authorization.restriction.GlobPattern
+ */
+public class L8_GlobRestrictionTest extends AbstractSecurityTest {
+
+
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/accesscontrol/L8_GlobRestrictionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L1_IntroductionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L1_IntroductionTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L1_IntroductionTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L1_IntroductionTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,259 @@
+/*
+ * 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.security.authorization.permission;
+
+import java.security.Principal;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.security.AccessControlManager;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.oak.util.NodeUtil;
+import org.junit.Test;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * <pre>
+ * Module: Authorization (Permission Evaluation)
+ * =============================================================================
+ *
+ * Title: Introduction
+ * -----------------------------------------------------------------------------
+ *
+ * Become familiar with the way to verify permissions using JCR API.
+ * Get a basic understanding how permission evaluation is used and exposed in Oak
+ * and finally gain insight into some details of the default implementation.
+ *
+ * Exercises:
+ *
+ * - Overview and Usages of Permission Evaluation
+ *   Search and list for permission lated methods in the JCR API and recap what
+ *   the specification states about permissions compared to access control
+ *   management.
+ *
+ *   Question: What are the areas in JCR that deal with permissions?
+ *   Question: Who is the expected API consumer?
+ *   Question: Can you elaborate when it actually makes sense to use this API?
+ *   Question: Can you think about potential drawbacks of doing so?
+ *
+ * - Permission Evaluation in Oak
+ *   In a second step try to become more familiar with the nature of the
+ *   permission evaluation in Oak.
+ *
+ *   Question: What is the nature of the public SPI?
+ *   Question: Can you identify the main entry point for permission evaluation?
+ *   Question: Can you identify to exact location(s) in Oak where read-access is being enforced?
+ *   Question: Can you identify the exact location(s) in Oak where all kind of write access is being enforced?
+ *
+ * - Configuration
+ *   Look at the default implementation(s) of the {@link org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration}
+ *   and try to identify the configurable parts with respect to permission evaluation.
+ *   Compare your results with the Oak documentation.
+ *
+ *   Question: Can you provide a list of configuration options for the permission evaluation?
+ *   Question: Can you identify where these configuration options are being evaluated?
+ *   Question: Which options also affect the access control management?
+ *
+ * - Pluggability
+ *   Become familar with the pluggable parts of the permission evaluation
+ *
+ *   Question: What means does Oak provide to change or extend the permission evaluation?
+ *   Question: Can you identify the interfaces that you needed to implement?
+ *   Question: Would it be possible to only replace the implementation of {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider}?
+ *             How could you achieve this?
+ *             And what would be the consequences for the whole authorization module?
+ *
+ *
+ * Advanced Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - Read Permission Walkthrough
+ *   Use {@link #testReadPermissionWalkThrough()} to become familiar with the
+ *   very internals of the permission evaluation. Walk through the item read
+ *   methods (for simplicity reduced to oak-core only) and be aware of
+ *   permission-evaluation related parts.
+ *
+ *   Question: Can you list the relevant steps wrt permission evalution?
+ *   Question: What is the nature of the returned tree objects?
+ *   Question: How can you verify if the editing test session can actually read those trees without using the permission-evalution code?
+ *   Question: How can you verify if the properties are accessible.
+ *
+ * - Write Permission Walkthrough
+ *   Use {@link #testWritePermissionWalkThrough()} to become familiar with the
+ *   internals of the permission evaluation with respect to writing. Walk through
+ *   the item write methods (for simplicity reduced to oak-core only) and the
+ *   subsequent {@link org.apache.jackrabbit.oak.api.Root#commit()} and be aware
+ *   of permission-evaluation related parts.
+ *
+ *   Question: Can you list the relevant steps wrt permission evalution?
+ *   Question: What can you say about write permissions for special (protected) items?
+ *
+ * - Extending {@link #testReadPermissionWalkThrough()} and {@link #testWritePermissionWalkThrough()}
+ *   Use the two test-cases and play with additional access/writes and or
+ *   additional (more complex) permission setup.
+ *
+ *
+ * Related Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link org.apache.jackrabbit.oak.security.authorization.permission.L2_PermissionDiscoveryTest}
+ *
+ * </pre>
+ */
+public class L1_IntroductionTest extends AbstractSecurityTest {
+
+    private ContentSession testSession;
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+        testSession = createTestSession();
+
+        Principal testPrincipal = getTestUser().getPrincipal();
+
+        NodeUtil rootNode = new NodeUtil(root.getTree("/"));
+        NodeUtil a = rootNode.addChild("a", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        a.setString("aProp", "aValue");
+
+        NodeUtil b = a.addChild("b", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        b.setString("bProp", "bValue");
+        // sibling
+        NodeUtil bb = a.addChild("bb", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        bb.setString("bbProp", "bbValue");
+
+        NodeUtil c = b.addChild("c", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        c.setString("cProp", "cValue");
+
+        setupPermission(root, "/a", testPrincipal, true, PrivilegeConstants.JCR_READ);
+        setupPermission(root, "/a/b", testPrincipal, true, PrivilegeConstants.JCR_ADD_CHILD_NODES);
+        setupPermission(root, "/a/bb", testPrincipal, false, PrivilegeConstants.REP_READ_PROPERTIES);
+        setupPermission(root, "/a/b/c", testPrincipal, true, PrivilegeConstants.REP_ADD_PROPERTIES);
+
+        root.commit();
+    }
+
+    @Override
+    public void after() throws Exception {
+        try {
+            if (testSession != null) {
+                testSession.close();
+            }
+            root.getTree("/a").remove();
+            root.commit();
+        } finally {
+            super.after();
+        }
+    }
+
+    /**
+     * Setup simple allow/deny permissions (without restrictions).
+     *
+     * @param root The editing root.
+     * @param path The path of the access controlled tree.
+     * @param principal The principal for which new ACE is being created.
+     * @param isAllow {@code true} if privileges are granted; {@code false} otherwise.
+     * @param privilegeNames The privilege names.
+     * @throws Exception If an error occurs.
+     */
+    private void setupPermission(@Nonnull Root root,
+                                 @Nullable String path,
+                                 @Nonnull Principal principal,
+                                 boolean isAllow,
+                                 @Nonnull String... privilegeNames) throws Exception {
+        AccessControlManager acMgr = getAccessControlManager(root);
+        JackrabbitAccessControlList acl = checkNotNull(AccessControlUtils.getAccessControlList(acMgr, path));
+        acl.addEntry(principal, AccessControlUtils.privilegesFromNames(acMgr, privilegeNames), isAllow);
+        acMgr.setPolicy(path, acl);
+        root.commit();
+    }
+
+    @Test
+    public void testReadPermissionWalkThrough() {
+        Root testRoot = testSession.getLatestRoot();
+
+        // EXERCISE verify if these tree are accessible using Tree#exists()
+        // Question: can you explain why using Tree.exists is sufficient and you don't necessarily need to perform the check on the PermissionProvider?
+        Tree rootTree = testRoot.getTree("/");
+        Tree bTree = testRoot.getTree("/a/b");
+        Tree cTree = testRoot.getTree("/a/b/c");
+
+        // EXERCISE verify if this is an accessible property? Q: how can you do this withouth testing the readability on the PermissionProvider?
+        PropertyState bProp = bTree.getProperty("bProp");
+        PropertyState bbProp = testRoot.getTree("/a/bb").getProperty("bbProp");
+        PropertyState cProp = cTree.getProperty("cProp");
+    }
+
+    @Test
+    public void testWritePermissionWalkThrough() throws CommitFailedException {
+        Root testRoot = testSession.getLatestRoot();
+
+        // EXERCISE walk through the test and fix it such that it passes.
+
+        Tree bTree = testRoot.getTree("/a/b");
+
+        // add a new child node at '/a/b' and persist the change to trigger the permission evaluation.
+        // EXERCISE: does it work with the current permission setup? if not, why (+ add exception handling)?
+        try {
+            Tree child = bTree.addChild("childName");
+            child.setProperty(JcrConstants.JCR_PRIMARYTYPE, NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+            testRoot.commit();
+        } finally {
+            testRoot.refresh();
+        }
+
+        // now change the primary type of the 'bTree'
+        // EXERCISE: does it work with the current permission setup? if not, why (+ add exception handling)?
+        try {
+            bTree.setProperty(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+            testRoot.commit();
+        } finally {
+            testRoot.refresh();
+        }
+
+        Tree cTree = testRoot.getTree("/a/b/c");
+
+        // now change the regula property 'cProp' of the 'cTree'
+        // EXERCISE: does it work with the current permission setup? if not, why (+ add exception handling)?
+        try {
+            cTree.setProperty("cProp", "changedValue");
+            testRoot.commit();
+        } finally {
+            testRoot.refresh();
+        }
+
+
+        // finally we try to add a new property to the 'cTree'
+        // EXERCISE: does it work with the current permission setup? if not, why (+ add exception handling)?
+        try {
+            cTree.setProperty("anotherCProp", "val");
+            testRoot.commit();
+        } finally {
+            root.refresh();
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L1_IntroductionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L2_PermissionDiscoveryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L2_PermissionDiscoveryTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L2_PermissionDiscoveryTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L2_PermissionDiscoveryTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,317 @@
+/*
+ * 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.security.authorization.permission;
+
+import java.security.Principal;
+import java.util.Map;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.Privilege;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+
+/**
+ * <pre>
+ * Module: Authorization (Permission Evaluation)
+ * =============================================================================
+ *
+ * Title: PermissionDiscoveryTest
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * Become familiar with the permission discovery as provided by {@link javax.jcr.Session}.
+ *
+ * Exercises:
+ *
+ * - Overview
+ *   Look at {@link javax.jcr.Session} and list the action constants that may
+ *   be used to test permissions of the editing session.
+ *   Compare this list with the built-in privileges defined in {@link javax.jcr.security.Privilege}
+ *   and explain the discrepancy.
+ *
+ *   Question: Can you find out with the predefined actions if you can lock an existing node?
+ *   Question: Can you find out with the predefined actions if you can register a new namespace?
+ *
+ * - {@link #testReadAccess()}
+ *   While there exists {@link javax.jcr.Session#ACTION_READ}, you can equally
+ *   use the direct methods to test for existance of a given item.
+ *   Use the test-case to learn about the difference and when using
+ *   {@link Session#hasPermission(String, String)} could actually make sense.
+ *
+ * - {@link #testModifyPermissions()}
+ *   Test illustrating the usage of {@link Session#ACTION_SET_PROPERTY}. Fill
+ *   in the expected values and explain why.
+ *
+ *   Question: How is {@link Session#ACTION_SET_PROPERTY} mapped to the internal permissions?
+ *   Question: Can make a table illustrating the effect of the individual permissions
+ *             (granted/denied) on the result depending on whether the item exists or not?
+ *
+ * - {@link #testRemovePermissions()}
+ *   Test illustrating the usage of {@link Session#ACTION_REMOVE}. Fill
+ *   in the expected values and explain why.
+ *
+ *   Question: How is {@link Session#ACTION_REMOVE} mapped to the internal permissions?
+ *   Question: Can make a table illustrating the effect of the individual permissions
+ *             (granted/denied) on the result depending on whether the item exists or not?
+ *   Question: Discuss what is special about the removal of nodes when
+ *             comparing the action, the privileges and the internal permissions
+ *
+ * - {@link #testAddPermissions()}
+ *   Test illustrating the usage of {@link Session#ACTION_ADD_NODE} and {@link Session#ACTION_SET_PROPERTY}
+ *   if used to create a new non-existing property. Fill in the expected values and explain why.
+ *
+ *   Question: How is {@link Session#ACTION_ADD_NODE} mapped to the internal permissions?
+ *   Question: Can make a table illustrating the effect of the individual permissions
+ *             (granted/denied) on the result depending on whether the item exists or not?
+ *   Question: Discuss what is special about the creation of new nodes when
+ *             comparing the action, the privileges and the internal permissions
+ *
+ * - {@link #testOakPermissions()}
+ *   The default permission implementation in Oak also allows for passing
+ *   string representation of the permission constants as 'actions'.
+ *   Adjust the test such that it passes.
+ *
+ *
+ * Additional Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - Session.checkPermission
+ *   Apart from {@link javax.jcr.Session#hasPermission(String, String)} there also
+ *   exists {@link javax.jcr.Session#checkPermission(String, String)}.
+ *
+ *   Question: Can you explain why it is generally recommended to use the non-throwing variant?
+ *
+ * - Explict vs. Builtin Permission Test
+ *
+ *   Question: Discuss why it is generally preferrable to leave the permission
+ *             evaluation to the repository instead of doing this manually in the application?
+ *   Question: Can you identify use-cases where this is nevertheless required?
+ *             How could they be avoided?
+ *
+ * </pre>
+ *
+ * @see javax.jcr.Session#hasPermission(String, String)
+ * @see javax.jcr.Session#checkPermission(String, String)
+ */
+public class L2_PermissionDiscoveryTest extends AbstractJCRTest {
+
+    private Principal testPrincipal;
+    private Session testSession;
+
+    private String childPath;
+    private String propertyPath;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Property p = testRootNode.setProperty(propertyName1, "val");
+        propertyPath = p.getPath();
+
+        Node child = testRootNode.addNode(nodeName1);
+        childPath = child.getPath();
+
+        User testUser = ExerciseUtility.createTestUser(((JackrabbitSession) superuser).getUserManager());
+        testPrincipal = testUser.getPrincipal();
+
+        Privilege[] privs = AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_READ, PrivilegeConstants.REP_ADD_PROPERTIES);
+        Privilege[] privs2 = AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_ADD_CHILD_NODES);
+        if (!AccessControlUtils.addAccessControlEntry(superuser, testRoot, testPrincipal, privs, true) ||
+            !AccessControlUtils.addAccessControlEntry(superuser, childPath, testPrincipal, privs2, true)) {
+            throw new NotExecutableException();
+        }
+
+        superuser.save();
+        testSession = superuser.getRepository().login(ExerciseUtility.getTestCredentials(testUser.getID()));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            if (testSession != null && testSession.isLive()) {
+                testSession.logout();
+            }
+            Authorizable testUser = ((JackrabbitSession) superuser).getUserManager().getAuthorizable(testPrincipal);
+            if (testUser != null) {
+                testUser.remove();
+                superuser.save();
+            }
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    private static Boolean[] existsAndHasPermission(Boolean expectedExists, Boolean expectedHasPermission) {
+        return new Boolean[] {expectedExists, expectedHasPermission};
+    }
+
+    public void testReadAccess() throws RepositoryException {
+        // EXERCISE: fill in the expected values
+        Map<String, Boolean[]> nodeTests = ImmutableMap.of(
+                "/", existsAndHasPermission(null, null),
+                testRoot, existsAndHasPermission(null, null),
+                childPath, existsAndHasPermission(null, null),
+                childPath + "/new", existsAndHasPermission(null, null)
+        );
+
+        for (String nodePath : nodeTests.keySet()) {
+            Boolean[] expected = nodeTests.get(nodePath);
+
+            assertEquals(expected[0].booleanValue(), testSession.nodeExists(nodePath));
+            assertEquals(expected[1].booleanValue(), testSession.hasPermission(nodePath, Session.ACTION_READ));
+        }
+
+        // EXERCISE: fill in the expected values
+        Map<String, Boolean[]> propertyTests = ImmutableMap.of(
+                "/jcr:primaryType", existsAndHasPermission(null, null),
+                propertyPath, existsAndHasPermission(null, null),
+                childPath + "/new", existsAndHasPermission(null, null)
+        );
+
+        for (String pPath : propertyTests.keySet()) {
+            Boolean[] expected = propertyTests.get(pPath);
+
+            assertEquals(expected[0].booleanValue(), testSession.nodeExists(pPath));
+            assertEquals(expected[1].booleanValue(), testSession.hasPermission(pPath, Session.ACTION_READ));
+        }
+    }
+
+    public void testModifyPermissions() throws RepositoryException {
+        // EXERCISE: fill in the expected values
+        Map<String, Boolean> modifyPropertyTests = ImmutableMap.of(
+                "/jcr:primaryType", null,
+                testRoot, null,
+                propertyPath, null
+        );
+        for (String pPath : modifyPropertyTests.keySet()) {
+            boolean canModifyProperty = modifyPropertyTests.get(pPath);
+            assertEquals(canModifyProperty, testSession.hasPermission(pPath, Session.ACTION_SET_PROPERTY));
+        }
+    }
+
+    public void testRemovePermissions() throws RepositoryException {
+        // EXERCISE: fill in the expected values
+        Map<String, Boolean> removePropertyTests = ImmutableMap.of(
+                "/jcr:primaryType", null,
+                propertyPath, null,
+                childPath + "/new", null
+        );
+        for (String pPath : removePropertyTests.keySet()) {
+            boolean canRemoveProperty = removePropertyTests.get(pPath);
+            assertEquals(canRemoveProperty, testSession.hasPermission(pPath, Session.ACTION_REMOVE));
+        }
+
+        // EXERCISE: fill in the expected values
+        Map<String, Boolean> removeNodesTests = ImmutableMap.of(
+                "/", null,
+                testRoot, null,
+                childPath, null,
+                childPath + "/new", null
+        );
+        for (String nodePath : removeNodesTests.keySet()) {
+            boolean canRemoveNode = removeNodesTests.get(nodePath);
+            assertEquals(canRemoveNode, testSession.hasPermission(nodePath, Session.ACTION_REMOVE));
+        }
+
+
+        // EXERCISE : change the permission setup such that the following tests succeed.
+        testSession.refresh(false);
+        assertTrue(testSession.hasPermission(childPath, Session.ACTION_REMOVE));
+        testSession.getNode(childPath).remove();
+        testSession.save();
+
+        // EXERCISE : change the permission setup such that the following tests succeed.
+
+        testSession.refresh(false);
+        assertTrue(testSession.hasPermission(propertyPath, Session.ACTION_REMOVE));
+        testSession.getProperty(propertyPath).remove();
+        testSession.save();
+
+    }
+
+    public void testAddPermissions() throws RepositoryException {
+        // EXERCISE: fill in the expected values
+        Map<String, Boolean> addPropertyTests = ImmutableMap.of(
+                "/propertyName1", null,
+                testRoot, null,
+                propertyPath, null,
+                childPath + "/new", null
+        );
+        for (String pPath : addPropertyTests.keySet()) {
+            boolean canAddProperty = addPropertyTests.get(pPath);
+            assertEquals(canAddProperty, testSession.hasPermission(pPath, Session.ACTION_SET_PROPERTY));
+        }
+
+        // EXERCISE: fill in the expected values
+        Map<String, Boolean> addNodesTests = ImmutableMap.of(
+                "/childNode", null,
+                testRoot, null,
+                testRoot + "/new", null,
+                childPath, null,
+                childPath + "/new", null
+        );
+        for (String childPath : addNodesTests.keySet()) {
+            boolean canAddNode = addNodesTests.get(childPath);
+            assertEquals(canAddNode, testSession.hasPermission(childPath, Session.ACTION_ADD_NODE));
+        }
+
+        // EXERCISE : change the permission setup such that the following tests succeed.
+
+        testSession.refresh(false);
+        testSession.getNode(testRoot).addNode(nodeName2);
+        testSession.save();
+    }
+
+    public void testOakPermissions() throws RepositoryException {
+        String modifyPropertyPermissions = null; // EXERCISE:
+        assertFalse(testSession.hasPermission(propertyPath, modifyPropertyPermissions));
+
+        // EXERCISE : modify the permission setup such that the following tests pass
+
+        testSession.refresh(false);
+        assertTrue(testSession.hasPermission(propertyPath, modifyPropertyPermissions));
+        assertFalse(testSession.hasPermission(propertyPath, Permissions.getString(Permissions.REMOVE_PROPERTY|Permissions.ADD_PROPERTY)));
+
+        String addItemPermissions = null; // EXERCISE
+        assertTrue(testSession.hasPermission(childPath, addItemPermissions));
+
+        String permissions = null; // EXERCISE
+        assertFalse(testSession.hasPermission(childPath, permissions));
+
+        // EXERCISE : modify the permission setup such that the following tests pass
+        assertFalse(testSession.hasPermission(testRoot, permissions));
+        assertTrue(testSession.hasPermission(childPath, permissions));
+
+        Node cNode = testSession.getNode(childPath);
+        cNode.addMixin(mixVersionable);
+        testSession.save();
+        cNode.checkin();
+        cNode.checkout();
+    }
+
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L2_PermissionDiscoveryTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L3_PrecedenceRulesTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L3_PrecedenceRulesTest.java?rev=1686235&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L3_PrecedenceRulesTest.java (added)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/security/authorization/permission/L3_PrecedenceRulesTest.java Thu Jun 18 14:30:16 2015
@@ -0,0 +1,262 @@
+/*
+ * 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.security.authorization.permission;
+
+import java.security.Principal;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.security.Privilege;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+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.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.security.ExerciseUtility;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.test.AbstractJCRTest;
+
+/**
+ * <pre>
+ * Module: Authorization (Permission Evaluation)
+ * =============================================================================
+ *
+ * Title: Basic Precedence Rules in Permission Evaluation
+ * -----------------------------------------------------------------------------
+ *
+ * Goal:
+ * The aim of this exercise is to make you familiar with some implementations
+ * details of the default permission evaluation.
+ *
+ * Exercises:
+ *
+ * - Overview
+ *   Read the on the Oak documentation about the default permission evaluation
+ *   implementation such that the following test cases are easy to solve.
+ *
+ *
+ * - {@link #testGroupMembership()}
+ *   Test illustrating that permissions granted/denied to groups are inherited
+ *   to the group members.
+ *
+ * - {@link #testHierarchy()}
+ *   Test illustrating that in the default implemenation permissions are inherited
+ *   though the item hierarchy.
+ *   Create the correct permission setup to verify this.
+ *
+ * - {@link #testAceOrder()}
+ *   This case shows how the order of ACEs within a given ACL affect the resulting
+ *   permissions. Fix the test case without dropping either of the two ACEs such
+ *   that the test passes and look at the ACEs present on the list before and
+ *   after the fix.
+ *
+ *   Question: How many ways to you find to fix the test?
+ *
+ * - {@link #testPrecedenceOfUserPrincipals()}
+ *   The goal of this test is to make you aware of the precendence of user principals
+ *   during permission evaluation.
+ *   Fix the test according to the instructions.
+ *
+ *   Question: How many ways to you find to fix the test?
+ *
+ * - {@link #testCombination()} and {@link #testCombination2()}
+ *   Additional tests combining the different rules testes above.
+ *   Fill in the correct values and explain the behaviour.
+ *
+ *
+ * Additional Exercise
+ * -----------------------------------------------------------------------------
+ *
+ * So far the test-cases only modify read permissions.
+ *
+ * - Write additional test-cases playing with different privileges
+ *
+ * - Once you feel comfortable with the basics include restrictions in your
+ *   tests and verify your expectations.
+ *
+ * - Create a test setting up permission at the 'null' path and describe the
+ *   result.
+ *   Question: What can you say about the inheritance rules you learned so far
+ *             when it comes to repository level permissions?
+ *
+ * HINT: there are plenty of test-cases present with oak-jcr and oak-core. Use
+ *       the tests already present to invent new exercises.
+ *
+ *
+ * </pre>
+ *
+ * @see <a href="http://jackrabbit.apache.org/oak/docs/security/permission/evaluation.html">Permission Evaluation in the Oak Docu</a>
+ */
+public class L3_PrecedenceRulesTest extends AbstractJCRTest {
+
+    private Principal testPrincipal;
+    private Principal testGroupPrincipal;
+    private Session testSession;
+
+    private String childPath;
+    private String propertyPath;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Property p = testRootNode.setProperty(propertyName1, "val");
+        propertyPath = p.getPath();
+
+        Node child = testRootNode.addNode(nodeName1);
+        childPath = child.getPath();
+
+        User testUser = ExerciseUtility.createTestUser(((JackrabbitSession) superuser).getUserManager());
+        Group testGroup = ExerciseUtility.createTestGroup(((JackrabbitSession) superuser).getUserManager());
+        testGroup.addMember(testUser);
+        superuser.save();
+
+        testPrincipal = testUser.getPrincipal();
+        testGroupPrincipal = testGroup.getPrincipal();
+
+        AccessControlUtils.addAccessControlEntry(superuser, testRoot, EveryonePrincipal.getInstance(), AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_ALL), false);
+
+        testSession = superuser.getRepository().login(ExerciseUtility.getTestCredentials(testUser.getID()));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            if (testSession != null && testSession.isLive()) {
+                testSession.logout();
+            }
+            UserManager uMgr = ((JackrabbitSession) superuser).getUserManager();
+            Authorizable testUser = uMgr.getAuthorizable(testPrincipal);
+            if (testUser != null) {
+                testUser.remove();
+            }
+            Authorizable testGroup = uMgr.getAuthorizable(testGroupPrincipal);
+            if (testGroup != null) {
+                testGroup.remove();
+            }
+            superuser.save();
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    public void testGroupMembership() throws RepositoryException {
+        assertFalse(testSession.nodeExists(testRoot));
+
+        assertTrue(((java.security.acl.Group) testGroupPrincipal).isMember(testPrincipal));
+
+        AccessControlUtils.addAccessControlEntry(superuser, testRoot, testGroupPrincipal, AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_READ), true);
+        superuser.save();
+
+        testSession.refresh(false);
+        boolean expected = false; // EXERCISE
+        assertEquals(expected, testSession.nodeExists(testRoot));
+    }
+
+    public void testHierarchy() throws RepositoryException {
+        assertFalse(testSession.nodeExists(testRoot));
+        assertFalse(testSession.nodeExists(childPath));
+        assertFalse(testSession.propertyExists(propertyPath));
+
+        Principal principal = testPrincipal;
+        // EXERCISE : create the correct permission setup such that the test session can read all items below.
+        // EXERCISE : how many entries do you need to create?
+        superuser.save();
+
+        testSession.refresh(false);
+        assertTrue(testSession.nodeExists(testRoot));
+        assertTrue(testSession.nodeExists(childPath));
+        assertTrue(testSession.propertyExists(propertyPath));
+    }
+
+    public void testAceOrder() throws RepositoryException {
+        assertFalse(testSession.nodeExists(testRoot));
+
+        Privilege[] readPrivs = AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_READ);
+
+        // EXERCISE: fix the permission setup such that the test success without dropping either ACE
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(superuser, testRoot);
+        acl.addEntry(testGroupPrincipal, readPrivs, true);
+        acl.addEntry(EveryonePrincipal.getInstance(), readPrivs, false);
+        superuser.getAccessControlManager().setPolicy(acl.getPath(), acl);
+        superuser.save();
+
+        testSession.refresh(false);
+        assertTrue(testSession.nodeExists(testRoot));
+        assertTrue(testSession.propertyExists(propertyPath));
+
+    }
+
+    public void testPrecedenceOfUserPrincipals() throws RepositoryException {
+        Privilege[] readPrivs = AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_READ);
+
+        JackrabbitAccessControlList acl = AccessControlUtils.getAccessControlList(superuser, testRoot);
+        acl.addEntry(testPrincipal, readPrivs, false);
+        acl.addEntry(testGroupPrincipal, readPrivs, true);
+        superuser.getAccessControlManager().setPolicy(acl.getPath(), acl);
+        superuser.save();
+
+        // EXERCISE what is the expected result?
+        testSession.refresh(false);
+        Boolean canRead = null; // EXERCISE
+        assertEquals(canRead.booleanValue(), testSession.nodeExists(testRoot));
+        assertEquals(canRead.booleanValue(), testSession.nodeExists(childPath));
+
+        // EXERCISE: now change the permission setup such that the testSession has read access
+        // EXERCISE: how many ways to you find to achieve this?
+    }
+
+    public void testCombination() throws RepositoryException {
+        Privilege[] readPrivs = AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_READ);
+
+        AccessControlUtils.addAccessControlEntry(superuser, testRoot, testPrincipal, readPrivs, false);
+        AccessControlUtils.addAccessControlEntry(superuser, childPath, testGroupPrincipal, readPrivs, true);
+        superuser.save();
+
+        // EXERCISE what is the expected result?
+        testSession.refresh(false);
+        Boolean canRead = null; // EXERCISE
+        assertEquals(canRead.booleanValue(), testSession.nodeExists(testRoot));
+        assertEquals(canRead.booleanValue(), testSession.propertyExists(propertyPath));
+        assertEquals(canRead.booleanValue(), testSession.nodeExists(childPath));
+    }
+
+    public void testCombination2() throws RepositoryException {
+        Privilege[] readPrivs = AccessControlUtils.privilegesFromNames(superuser, Privilege.JCR_READ);
+
+        AccessControlUtils.addAccessControlEntry(superuser, testRoot, testPrincipal, readPrivs, false);
+        AccessControlUtils.addAccessControlEntry(superuser, testRoot, testPrincipal, new String[] {PrivilegeConstants.REP_READ_PROPERTIES}, true);
+        AccessControlUtils.addAccessControlEntry(superuser, childPath, testGroupPrincipal, readPrivs, false);
+        superuser.save();
+
+        // EXERCISE what is the expected result?
+        testSession.refresh(false);
+        Boolean canRead = null; // EXERCISE
+        assertEquals(canRead.booleanValue(), testSession.nodeExists(testRoot));
+        canRead = null; // EXERCISE
+        assertEquals(canRead.booleanValue(), testSession.propertyExists(propertyPath));
+        canRead = null; // EXERCISE
+        assertEquals(canRead.booleanValue(), testSession.nodeExists(childPath));
+        canRead = null; // EXERCISE
+        assertEquals(canRead.booleanValue(), testSession.propertyExists(childPath+"/jcr:primaryType"));
+    }
+}
\ No newline at end of file