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 2018/04/19 15:22:47 UTC

svn commit: r1829562 [2/2] - in /jackrabbit/oak/trunk/oak-exercise/src: main/java/org/apache/jackrabbit/oak/exercise/security/authorization/models/predefined/ main/java/org/apache/jackrabbit/oak/exercise/security/authorization/models/readonly/ main/jav...

Copied: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L4_CustomAccessControlManagementTest.java (from r1829512, jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L5_CustomAccessControlManagementTest.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L4_CustomAccessControlManagementTest.java?p2=jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L4_CustomAccessControlManagementTest.java&p1=jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L5_CustomAccessControlManagementTest.java&r1=1829512&r2=1829562&rev=1829562&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L5_CustomAccessControlManagementTest.java (original)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L4_CustomAccessControlManagementTest.java Thu Apr 19 15:22:47 2018
@@ -16,6 +16,58 @@
  */
 package org.apache.jackrabbit.oak.exercise.security.authorization.advanced;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.AccessControlPolicyIterator;
+import javax.jcr.security.NamedAccessControlPolicy;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+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.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.exercise.security.authorization.models.simplifiedroles.ThreeRolesAuthorizationConfiguration;
+import org.apache.jackrabbit.oak.exercise.security.authorization.models.simplifiedroles.ThreeRolesConstants;
+import org.apache.jackrabbit.oak.exercise.security.principal.CustomPrincipalConfiguration;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.jcr.repository.RepositoryImpl;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration;
+import org.apache.jackrabbit.oak.security.internal.SecurityProviderHelper;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration;
+import org.apache.jackrabbit.oak.spi.whiteboard.DefaultWhiteboard;
+import org.apache.jackrabbit.oak.spi.xml.ProtectedNodeImporter;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 /**
  * <pre>
  * Module: Advanced Authorization Topics
@@ -25,14 +77,449 @@ package org.apache.jackrabbit.oak.exerci
  * -----------------------------------------------------------------------------
  *
  * Goal:
- * TODO
+ * Learn how to write your own access control management and how to properly
+ * secure access control content.
+ * The exercises is this lesson will make use of a authorization model stub that
+ * already has the permission evaluation implemented. The entry point of that model
+ * is {@link ThreeRolesAuthorizationConfiguration}.
  *
  * Exercises:
- * TODO
+ *
+ * - {@link #testGetPolicies()}
+ *   Complete the implementation of {@link AccessControlManager#getPolicies(String)}
+ *   such that the test passes.
+ *   Adjust the number of expected policies and the type of policies according
+ *   to your implementation.
+ *
+ *   Questions:
+ *   - what type of policy do you want to expose?
+ *   - does any of the existing types of access control policies fit your needs?
+ *     existing types include
+ *     > {@link NamedAccessControlPolicy},
+ *     > {@link javax.jcr.security.AccessControlList},
+ *     > {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlList},
+ *     > {@link org.apache.jackrabbit.api.security.authorization.PrincipalSetPolicy}
+ *   - if you choose to define your own policy type: what does it look like?
+ *   - should {@link AccessControlManager#getPolicies(String)} return one or many policies?
+ *
+ * - {@link #testGetEffectivePolicies()}
+ *   Complete the implementation of {@link AccessControlManager#getEffectivePolicies(String)}
+ *   such that the test passes.
+ *   NOTE: computation of effective policies is specified to be a best-effort operation.
+ *
+ *   Questions:
+ *   - what type of policy do you want to expose?
+ *   - what are the effective policies for those nodes that don't have the custom
+ *     policy set? or in other words: which nodes are affected by the policy set
+ *     at /test/a in the test setup?
+ *   - does the set of effective policies include any default policies that have
+ *     not been explicit set?
+ *   - what's the maximal number of effective policies your implementation may return?
+ *
+ * - {@link #testGetApplicablePolicies()}
+ *   Complete the implementation of {@link AccessControlManager#testGetApplicablePolicies(String)}
+ *   such that the test passes.
+ *
+ *   Questions:
+ *   - what type of policies do you expose here?
+ *   - are they they same as with {@link #testGetPolicies()} and/or {@link #testGetEffectivePolicies()}?
+ *   - does /test/a still have applicable policies?
+ *   - what about the path outside of the tree defined by 'supportedPath' configuration option?
+ *   - as you learned in the previous section and can see in {@link org.apache.jackrabbit.oak.exercise.security.authorization.models.simplifiedroles.ThreeRolesPermissionProvider}
+ *     this simplified authorization model doesn't respect nesting of policies in a
+ *     given tree. what does that mean for the applicable policies?
+ *   - what's the maximal number of applicable policies your implementation may return at a given path?
+ *
+ * - {@link #testSetPolicy()}
+ *   Implement {@link AccessControlManager#setPolicy(String, AccessControlPolicy)} and
+ *   {@link PolicyOwner#defines(String, AccessControlPolicy)} such that policies
+ *   can be written to the repository in a composite authorization setup.
+ *   The {@link PolicyOwner} is also required for the subsequent tests.
+ *
+ * - {@link #testSetModifiedPolicy()}
+ *   Modify the custom access control setup at /test/a such that the principal
+ *   associated with the test-user get moved from the editor to the owner set.
+ *
+ * - {@link #testRemovePolicy()}
+ *   Implement {@link AccessControlManager#removePolicy(String, AccessControlPolicy)} such
+ *   that the test passes.
+ *
+ * - {@link #testAccessControlContentIsProtected()}
+ *   Your authorization setup should come with some validation of the access control
+ *   content written to the repository.
+ *   Write a {@link org.apache.jackrabbit.oak.spi.commit.ValidatorProvider} and
+ *   plug it into the authorization configuration such that the test-case passes
+ *   and discuss each of the assertions made.
+ *
+ *   Questions:
+ *   - Can you identify possible shortcomings with the 4 validation steps proposed?
+ *   - Under which circumstances could any of them might not be desirable?
+ *
+ * - {@link #testAccessControlItemsAreProtectedByNodeTypeDefinition()}
+ *   Identify the code in the simplifiedroles authorization model that makes sure
+ *   the simple policy node and it's properties have JCR item definitions that are
+ *   protected.
+ *
+ *   Discuss why this is needed and what the effect of this measure is.
+ *   Complete the test case by
+ *   - testing the protected status of access control content using JCR API calls.
+ *   - verifying the protected status using JCR write API
+ *
+ *   Question:
+ *   - Can you identify which parts of Oak are responsible for enforcing the protected status?
+ *
+ * - {@link #testImportNodeWithPolicy}
+ *   This is a follow-up on {@link #testAccessControlItemsAreProtectedByNodeTypeDefinition()} as
+ *   the protected item definitions are not only enforced upon regular write
+ *   operations but also when calling {@link javax.jcr.Session#importXML(String, InputStream, int)},
+ *   {@link javax.jcr.Workspace#importXML(String, InputStream, int)} and related calls.
+ *
+ *   Fix the simplifiedroles authorization model such that the test passes.
+ *   Hint: you need to implement a custom implementation of {@link ProtectedNodeImporter}
+ *   and ensure the {@link AuthorizationConfiguration#getProtectedItemImporters()}
+ *   exposes it to the security setup.
+ *
+ * - {@link #testImportNodeWithPolicyAndUnknownPrincipal}
+ *   Variant of {@link #testImportNodeWithPolicy} that attempts to import a policy
+ *   referring to an {@code Principal} that is not known to any of the providers.
+ *
+ *   Questions:
+ *   - What do you need to do to the setup and possibly your importer code such
+ *     that importing unknown principals is allowed?
+ *   - In case your importer didn't check the validity of the principals:
+ *     Discuss the adjustments you would need to make to your importer in order
+ *     to enforce the different levels of validation check.
+ *
+ *
+ * Advanced Exercise
+ * -----------------------------------------------------------------------------
+ *
+ * The AccessControlManager stub doesn't implement {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlManager}.
+ * Take a look at the additional methods defined by the extension in Jackrabbit API.
+ *
+ * As you can see the extra methods all related to access control management by
+ * {@link Principal}.
+ *
+ * Questions:
+ * - Would it be possible/sensible to have your implementation additionally implement
+ *   the methods {@link org.apache.jackrabbit.api.security.JackrabbitAccessControlManager}?
+ *
+ * - If you think it's possible, what would the implementation look like?
+ *
+ * - Can you spot any obstacles with that approach?
+ *
+ *
+ * Related Exercises:
+ * -----------------------------------------------------------------------------
+ *
+ * - {@link L5_CustomPermissionEvaluationTest}
  *
  * </pre>
  */
-public class L5_CustomAccessControlManagementTest {
+public class L4_CustomAccessControlManagementTest extends AbstractSecurityTest {
+
+    @Override
+    protected SecurityProvider initSecurityProvider() {
+        ThreeRolesAuthorizationConfiguration threeRolesAuthorizationConfiguration = new ThreeRolesAuthorizationConfiguration();
+        threeRolesAuthorizationConfiguration.setParameters(ConfigurationParameters.of("supportedPath", "/test"));
+
+        CustomPrincipalConfiguration pc = new CustomPrincipalConfiguration();
+        pc.setParameters(ConfigurationParameters.of("knownPrincipals", new String[] {"principalR", "principalE", "principalO"}));
+
+        SecurityProvider sp = super.initSecurityProvider();
+        SecurityProviderHelper.updateConfig(sp, threeRolesAuthorizationConfiguration, AuthorizationConfiguration.class);
+        SecurityProviderHelper.updateConfig(sp, pc, PrincipalConfiguration.class);
+
+        return sp;
+    }
+
+    @Override
+    protected ConfigurationParameters getSecurityConfigParameters() {
+        return ConfigurationParameters.of("authorizationCompositionType", CompositeAuthorizationConfiguration.CompositionType.OR.toString());
+    }
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        Tree testTree = TreeUtil.addChild(root.getTree("/"), "test", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        Tree aTree = TreeUtil.addChild(testTree, "a", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        aTree.setProperty("aProp", "value");
+
+        Tree abTree = TreeUtil.addChild(aTree, "b", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        abTree.setProperty("abProp", "value");
+
+
+        TreeUtil.addMixin(aTree, ThreeRolesConstants.MIX_REP_THREE_ROLES_POLICY, root.getTree(NodeTypeConstants.NODE_TYPES_PATH), null);
+        Tree rolePolicy = TreeUtil.addChild(aTree, ThreeRolesConstants.REP_3_ROLES_POLICY, ThreeRolesConstants.NT_REP_THREE_ROLES_POLICY);
+        rolePolicy.setProperty(ThreeRolesConstants.REP_READERS, ImmutableSet.of("principalR", EveryonePrincipal.NAME), Type.STRINGS);
+        rolePolicy.setProperty(ThreeRolesConstants.REP_EDITORS, ImmutableSet.of("principalE",getTestUser().getPrincipal().getName()), Type.STRINGS);
+        rolePolicy.setProperty(ThreeRolesConstants.REP_OWNERS,  ImmutableSet.of("principalO"), Type.STRINGS);
+
+        // add one node outside the scope of the supported path
+        Tree outside = TreeUtil.addChild(root.getTree("/"), "outside", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+
+        root.commit();
+
+        // to verify that setup with CompositeAuthorizationConfiguration.CompositionType.OR
+        // uncomment the lines below
+        /*
+        try (ContentSession cs = createTestSession()) {
+            Root r = createTestSession().getLatestRoot();
+            assertTrue(r.getTree("/test/a").exists());
+        }
+        */
+    }
+
+    private AccessControlManager getAcManager(@Nonnull Root root) {
+        return getConfig(AuthorizationConfiguration.class).getAccessControlManager(root, NamePathMapper.DEFAULT);
+    }
+
+    private Repository buildJcrRepository() {
+        return new RepositoryImpl(
+                getContentRepository(),
+                new DefaultWhiteboard(),
+                getSecurityProvider(),
+                Jcr.DEFAULT_OBSERVATION_QUEUE_LENGTH,
+                null,
+                false);
+    }
+
+
+    /**
+     * EXERCISE: complete {@link AccessControlManager#getPolicies(String)} such that
+     * the policy that has been 'manually' created in the setup is properly exposed
+     * by the access control management API.
+     */
+    @Test
+    public void testGetPolicies() throws Exception {
+        AccessControlPolicy[] policies = getAcManager(root).getPolicies("/test/a");
+
+        int len = -1; // EXERCISE: set expected length. 1 is the minimum but there might be more.
+        assertEquals(len, policies.length);
+
+        // EXERCISE: additionally assert that the policies is of the type you defined
+        for (int i = 0; i < len; i++) {
+            assertTrue(policies[i] instanceof AccessControlPolicy); // EXERCISE: replace by type chosen!
+        }
+    }
+
+    @Test
+    public void testGetEffectivePolicies() throws Exception {
+        // EXERCISE: set expected number of effective policies for all paths in the map.
+        Map<String,Integer> m = ImmutableMap.of("/", -1, "/test", -1, "/test/a/b", -1, "/outside", -1);
+
+        for (String path : m.keySet()) {
+            AccessControlPolicy[] policies = getAcManager(root).getEffectivePolicies(path);
+
+            int len = m.get(path); // EXERCISE: set expected length. 1 is the minimum but there might be more.
+            assertEquals(len, policies.length);
+
+            // EXERCISE: additionally assert that the policies is of the type you defined
+            for (int i = 0; i < len; i++) {
+                assertTrue(policies[i] instanceof AccessControlPolicy); // EXERCISE: replace by type chosen!
+            }
+        }
+    }
+
+    @Test
+    public void testGetApplicablePolicies() throws Exception {
+        // EXERCISE: set expected number of applicable policies for all paths in the map.
+        Map<String,Integer> m = ImmutableMap.of("/test/a", -1, "/test/a/b", -1, "/outside", -1);
+
+        for (String path : m.keySet()) {
+            AccessControlPolicyIterator it = getAcManager(root).getApplicablePolicies(path);
+            assertEquals(m.get(path).longValue(), it.getSize());
+
+            // EXERCISE: additionally assert that the policies is of the type you defined
+            while (it.hasNext()) {
+                assertTrue(it.nextAccessControlPolicy() instanceof AccessControlPolicy); // EXERCISE: replace by type chosen!
+            }
+        }
+    }
+
+    @Test
+    public void testSetPolicy() throws Exception {
+        Tree t = TreeUtil.addChild(root.getTree("/test"), "another", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        AccessControlManager acMgr = getAcManager(root);
+        AccessControlPolicy[] policies = acMgr.getPolicies(t.getPath());
+
+        // EXERCISE: set your custom policy/policies at /test/another such that
+        //           the following assertions pass.
+        // ... write your code here
+
+        root.commit();
+
+        PrincipalManager pm = getPrincipalManager(root);
+        Map<Principal,Long> m = ImmutableMap.of(
+                getTestUser().getPrincipal(), Permissions.NO_PERMISSION,
+                pm.getEveryone(), Permissions.NO_PERMISSION,
+                pm.getPrincipal("principalR"), Permissions.READ,
+                pm.getPrincipal("principalE"), Permissions.NO_PERMISSION,
+                pm.getPrincipal("principalO"), ThreeRolesConstants.SUPPORTED_PERMISSIONS
+        );
+        for (Principal principal : m.keySet()) {
+            PermissionProvider pp = getConfig(AuthorizationConfiguration.class).getPermissionProvider(root, adminSession.getWorkspaceName(), ImmutableSet.of(principal));
+            assertTrue(pp.isGranted(t, null, m.get(principal)));
+        }
+    }
+
+    @Test
+    public void testSetModifiedPolicy() throws Exception {
+        AccessControlManager acMgr = getAcManager(root);
+        AccessControlPolicy[] policies = acMgr.getPolicies("/test/a");
+
+        // EXERCISE: modify policies such that the testuser principal becomes owner instead of editor
+        // ... write your code here
+
+        for (AccessControlPolicy policy : policies) {
+            acMgr.setPolicy("/test/a", policy);
+        }
+        root.commit();
+
+        try (ContentSession cs = createTestSession()) {
+            Root r = createTestSession().getLatestRoot();
+            PermissionProvider pp = getConfig(AuthorizationConfiguration.class).getPermissionProvider(r, cs.getWorkspaceName(), cs.getAuthInfo().getPrincipals());
+            assertTrue(pp.isGranted("/test/a", Permissions.getString(ThreeRolesConstants.SUPPORTED_PERMISSIONS)));
+        }
+    }
+
+    @Test
+    public void testRemovePolicy() throws Exception {
+        AccessControlManager acMgr = getAcManager(root);
+        for (AccessControlPolicy policy : acMgr.getPolicies("/test/a")) {
+            acMgr.removePolicy("/test/a", policy);
+        }
+        root.commit();
+        assertEquals(0, acMgr.getPolicies("/test/a").length);
+
+    }
+
+    @Test
+    public void testAccessControlContentIsProtected() throws Exception {
+        Tree test = root.getTree("/test");
+
+        try {
+            Tree missingMixin = TreeUtil.addChild(test, ThreeRolesConstants.REP_3_ROLES_POLICY, ThreeRolesConstants.NT_REP_THREE_ROLES_POLICY);
+            root.commit();
+            fail("Adding policy without mixin must fail.");
+        } catch (CommitFailedException e) {
+            // success
+        }
+
+        try {
+            test.setProperty(ThreeRolesConstants.REP_OWNERS, 437);
+            root.commit();
+            fail("Using name of protected policy property outside of the context of a policy must fail.");
+        } catch (CommitFailedException e) {
+            // success
+        }
+
+        try {
+            Tree b = root.getTree("/test/a/b");
+            TreeUtil.addMixin(b, ThreeRolesConstants.MIX_REP_THREE_ROLES_POLICY, root.getTree(NodeTypeConstants.NODE_TYPES_PATH), null);
+            Tree nestedPolicy = TreeUtil.addChild(b, ThreeRolesConstants.REP_3_ROLES_POLICY, ThreeRolesConstants.NT_REP_THREE_ROLES_POLICY);
+            root.commit();
+
+            fail("Creation of nested three-roles-policy must fail (NOTE: this is an arbitrary limitation for the sake of simplifying permission evaluation).");
+        } catch (CommitFailedException e) {
+            // success
+        }
+
+        try {
+            Tree outside = root.getTree("/outside");
+            TreeUtil.addMixin(outside, ThreeRolesConstants.MIX_REP_THREE_ROLES_POLICY, root.getTree(NodeTypeConstants.NODE_TYPES_PATH), null);
+            Tree nestedPolicy = TreeUtil.addChild(outside, ThreeRolesConstants.REP_3_ROLES_POLICY, ThreeRolesConstants.NT_REP_THREE_ROLES_POLICY);
+            root.commit();
+
+            fail("Creation of nested three-roles-policy outside of the configured supported path must fail.");
+        } catch (CommitFailedException e) {
+            // success
+        }
+
+    }
+
+    @Test
+    public void testAccessControlItemsAreProtectedByNodeTypeDefinition() throws Exception {
+        ReadOnlyNodeTypeManager ntMgr = ReadOnlyNodeTypeManager.getInstance(root, NamePathMapper.DEFAULT);
+
+        Tree aTree = root.getTree("/test/a");
+        Tree policyTree = aTree.getChild(ThreeRolesConstants.REP_3_ROLES_POLICY);
+
+        NodeDefinition policyDef = ntMgr.getDefinition(aTree, policyTree);
+        assertTrue(policyDef.isProtected());
+
+        for (String propName : new String[] {ThreeRolesConstants.REP_READERS, ThreeRolesConstants.REP_EDITORS, ThreeRolesConstants.REP_OWNERS}) {
+            PropertyDefinition propDef = ntMgr.getDefinition(policyTree, policyTree.getProperty(propName), true);
+            assertTrue(propDef.isProtected());
+        }
+
+        Repository jcrRepository = buildJcrRepository();
+
+        // EXERCISE: test protected status of items using JCR API calls
+        // EXERCISE: verify that the protected status of the access control content is enforced
+        // ... write your code here
+    }
+
+    @Test
+    public void testImportNodeWithPolicy() throws Exception {
+        Repository jcrRepository = new RepositoryImpl(
+                getContentRepository(),
+                new DefaultWhiteboard(),
+                getSecurityProvider(),
+                Jcr.DEFAULT_OBSERVATION_QUEUE_LENGTH,
+                null,
+                false);
+
+        Session adminSession = jcrRepository.login(getAdminCredentials(), null);
+        try {
+            String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+                    "<sv:node sv:name=\"another2\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                        "<sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>oak:Unstructured</sv:value></sv:property>" +
+                        "<sv:property sv:name=\"jcr:mixinTypes\" sv:type=\"Name\"><sv:value>rep:ThreeRolesMixin</sv:value></sv:property>" +
+                        "<sv:node sv:name=\"rep:threeRolesPolicy\" " +
+                            "<sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:ThreeRolesPolicy</sv:value></sv:property>" +
+                            "<sv:property sv:name=\"rep:readers\" sv:type=\"String\"><sv:value>principalR</sv:value></sv:property>" +
+                        "</sv:node>" +
+                    "</sv:node>";
+
+            adminSession.importXML("/test", new ByteArrayInputStream(xml.getBytes()), ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+
+            Node n = adminSession.getNode("/test/another");
+            AccessControlPolicy[] policies = adminSession.getAccessControlManager().getPolicies("/test/another");
+            assertTrue(policies.length > 0);
+
+        } finally {
+            adminSession.refresh(false);
+            adminSession.logout();
+        }
+    }
+
+    @Test
+    public void testImportNodeWithPolicyAndUnknownPrincipal() throws Exception {
+        Repository jcrRepository = buildJcrRepository();
+
+        Session adminSession = jcrRepository.login(getAdminCredentials(), null);
+        try {
+            String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+                    "<sv:node sv:name=\"another2\" xmlns:mix=\"http://www.jcp.org/jcr/mix/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" xmlns:fn_old=\"http://www.w3.org/2004/10/xpath-functions\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" xmlns:rep=\"internal\" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\">" +
+                        "<sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>oak:Unstructured</sv:value></sv:property>" +
+                        "<sv:property sv:name=\"jcr:mixinTypes\" sv:type=\"Name\"><sv:value>rep:ThreeRolesMixin</sv:value></sv:property>" +
+                        "<sv:node sv:name=\"rep:threeRolesPolicy\" " +
+                            "<sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\"><sv:value>rep:ThreeRolesPolicy</sv:value></sv:property>" +
+                            "<sv:property sv:name=\"rep:readers\" sv:type=\"String\"><sv:value>unknownPrincipal</sv:value></sv:property>" +
+                        "</sv:node>" +
+                    "</sv:node>";
+
+            adminSession.importXML("/test", new ByteArrayInputStream(xml.getBytes()), ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
 
+            Node n = adminSession.getNode("/test/another");
+            AccessControlPolicy[] policies = adminSession.getAccessControlManager().getPolicies("/test/another");
+            assertTrue(policies.length > 0);
 
+        } finally {
+            adminSession.refresh(false);
+            adminSession.logout();
+        }
+    }
 }
\ No newline at end of file

Copied: jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L5_CustomPermissionEvaluationTest.java (from r1829512, jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L6_CustomPermissionEvaluationTest.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L5_CustomPermissionEvaluationTest.java?p2=jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L5_CustomPermissionEvaluationTest.java&p1=jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L6_CustomPermissionEvaluationTest.java&r1=1829512&r2=1829562&rev=1829562&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L6_CustomPermissionEvaluationTest.java (original)
+++ jackrabbit/oak/trunk/oak-exercise/src/test/java/org/apache/jackrabbit/oak/exercise/security/authorization/advanced/L5_CustomPermissionEvaluationTest.java Thu Apr 19 15:22:47 2018
@@ -16,6 +16,43 @@
  */
 package org.apache.jackrabbit.oak.exercise.security.authorization.advanced;
 
+import java.security.Principal;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.jcr.GuestCredentials;
+import javax.jcr.Session;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.apache.jackrabbit.oak.AbstractSecurityTest;
+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.commons.PathUtils;
+import org.apache.jackrabbit.oak.exercise.security.authorization.models.predefined.PredefinedAuthorizationConfiguration;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.security.authentication.AuthenticationConfigurationImpl;
+import org.apache.jackrabbit.oak.security.authentication.token.TokenConfigurationImpl;
+import org.apache.jackrabbit.oak.security.internal.SecurityProviderBuilder;
+import org.apache.jackrabbit.oak.security.principal.PrincipalConfigurationImpl;
+import org.apache.jackrabbit.oak.security.privilege.PrivilegeConfigurationImpl;
+import org.apache.jackrabbit.oak.security.user.UserConfigurationImpl;
+import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants;
+import org.apache.jackrabbit.util.Text;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 /**
  * <pre>
  * Module: Advanced Authorization Topics
@@ -25,14 +62,144 @@ package org.apache.jackrabbit.oak.exerci
  * -----------------------------------------------------------------------------
  *
  * Goal:
- * TODO
+ * Write a custom {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider}
+ * for a predefined requirement in order to become familiar with the details of
+ * the Oak permission evaluation.
  *
  * Exercises:
- * TODO
+ *
+ * Complete the implementation of {@link org.apache.jackrabbit.oak.exercise.security.authorization.models.predefined.PredefinedPermissionProvider}
+ * such that the tests pass.
+ *
+ *
+ * Advanced Exercise
+ * -----------------------------------------------------------------------------
+ *
+ * Currently the {@link org.apache.jackrabbit.oak.exercise.security.authorization.models.predefined.PredefinedPermissionProvider}
+ * doesn't implement {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider} interface
+ * and can therefore not be used in a setup that combines multipe authorization models.
+ *
+ * As an advanced exercise modify the {@link org.apache.jackrabbit.oak.exercise.security.authorization.models.predefined.PredefinedPermissionProvider}
+ * to additionally implement {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider}
+ * and deploy the {@link PredefinedAuthorizationConfiguration} in a setup with
+ * multiple authorization models.
+ *
+ * - Discuss the additional methods defined by {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider}.
+ * - Clarify which type of 'Authorization Composition' your implementation should be used.
+ * - Observe the result of your combination and explain the results to effective permissions.
  *
  * </pre>
  */
-public class L6_CustomPermissionEvaluationTest {
+public class L5_CustomPermissionEvaluationTest extends AbstractSecurityTest {
+
+    private static final String[] ACTION_NAMES = new String[] {
+            Session.ACTION_READ, Session.ACTION_ADD_NODE, Session.ACTION_SET_PROPERTY, Session.ACTION_REMOVE
+    };
+
+    private List<Tree> trees;
+    private PropertyState prop;
+
+    @Override
+    protected SecurityProvider initSecurityProvider() {
+        AuthorizationConfiguration ac = new PredefinedAuthorizationConfiguration();
+
+        return SecurityProviderBuilder.newBuilder().with(
+                new AuthenticationConfigurationImpl(), ConfigurationParameters.EMPTY,
+                new PrivilegeConfigurationImpl(), ConfigurationParameters.EMPTY,
+                new UserConfigurationImpl(), ConfigurationParameters.EMPTY,
+                ac, ConfigurationParameters.EMPTY,
+                new PrincipalConfigurationImpl(), ConfigurationParameters.EMPTY,
+                new TokenConfigurationImpl(), ConfigurationParameters.EMPTY)
+                .with(getSecurityConfigParameters())
+                .withRootProvider(getRootProvider())
+                .withTreeProvider(getTreeProvider())
+                .build();
+    }
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+
+        prop = PropertyStates.createProperty("prop", "value");
+
+        Tree testTree = TreeUtil.addChild(root.getTree("/"), "contentA", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        Tree aTree = TreeUtil.addChild(testTree, "a", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        aTree.setProperty(prop);
+
+        Tree aaTree = TreeUtil.addChild(aTree, "a", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        aaTree.setProperty(prop);
+
+        Tree bTree = TreeUtil.addChild(root.getTree("/"), "contentB", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        bTree.setProperty(prop);
+
+        Tree bbTree = TreeUtil.addChild(bTree, "b", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        bbTree.setProperty(prop);
+
+        Tree cTree = TreeUtil.addChild(root.getTree("/"), "contentC", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        cTree.setProperty(prop);
+
+        Tree ccTree = TreeUtil.addChild(cTree, "c", NodeTypeConstants.NT_OAK_UNSTRUCTURED);
+        ccTree.setProperty(prop);
+
+        root.commit();
+
+        trees = ImmutableList.<Tree>builder().add(root.getTree("/")).add(testTree).add(aTree).add(aaTree).add(bTree).add(bbTree).add(cTree).add(ccTree).build();
+
+    }
+
+    private PermissionProvider getPermissionProvider(@Nonnull Set<Principal> principals) {
+        return getConfig(AuthorizationConfiguration.class).getPermissionProvider(root, adminSession.getWorkspaceName(), principals);
+    }
+
+    private Iterable<String> getTreePaths() {
+        return Iterables.transform(trees, Tree::getPath);
+    }
+
+    @Test
+    public void testAdministratorHasFullAccessEverywhere() {
+        for (String path : getTreePaths()) {
+            Tree t = root.getTree(path);
+            assertFalse(t.exists());
+        }
+
+        PermissionProvider pp = getPermissionProvider(adminSession.getAuthInfo().getPrincipals());
+        for (Tree t : trees) {
+            pp.getPrivileges(t).contains(PrivilegeConstants.JCR_ALL);
+            assertTrue(pp.isGranted(t, null, Permissions.ALL));
+            assertTrue(pp.isGranted(t, prop, Permissions.ALL));
+
+            String treePath = t.getPath();
+            String allActions = Text.implode(ACTION_NAMES, ",");
+            assertTrue(pp.isGranted(treePath, allActions));
+            assertTrue(pp.isGranted(PathUtils.concat(treePath, prop.getName()), allActions));
+        }
+    }
+
+    @Test
+    public void testGuestHasNowherePermissions() throws Exception {
+        try (ContentSession guest = login(new GuestCredentials())) {
+            Root r = guest.getLatestRoot();
+            for (String path : getTreePaths()) {
+                Tree t = r.getTree(path);
+                assertFalse(t.exists());
+            }
+
+            PermissionProvider pp = getPermissionProvider(guest.getAuthInfo().getPrincipals());
+            for (Tree t : trees) {
+                pp.getPrivileges(t).isEmpty();
+                for (long permission : Permissions.aggregates(Permissions.ALL)) {
+                    assertFalse(pp.isGranted(t, null, permission));
+                    assertFalse(pp.isGranted(t, prop, permission));
+                }
 
+                for (String action : ACTION_NAMES) {
+                    String treePath = t.getPath();
+                    assertFalse(pp.isGranted(treePath, action));
+                    assertFalse(pp.isGranted(PathUtils.concat(treePath, prop.getName()), action));
+                }
+            }
+        }
+    }
 
+    // TODO: add more tests
 }
\ No newline at end of file