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
