| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.jackrabbit.oak.exercise.security.authorization.advanced; |
| |
| import javax.jcr.GuestCredentials; |
| import javax.jcr.security.AccessControlList; |
| import javax.jcr.security.AccessControlManager; |
| import javax.jcr.security.AccessControlPolicy; |
| import javax.jcr.security.AccessControlPolicyIterator; |
| |
| import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; |
| import org.apache.jackrabbit.api.security.authorization.PrincipalSetPolicy; |
| 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.memory.PropertyStates; |
| 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.namespace.NamespaceConstants; |
| 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.cug.impl.CugConfiguration; |
| import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal; |
| import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConstants; |
| import org.junit.Test; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| |
| /** |
| * <pre> |
| * Module: Advanced Authorization Topics |
| * ============================================================================= |
| * |
| * Title: Aggregating Multiple Authorization Models : How It Works |
| * ----------------------------------------------------------------------------- |
| * |
| * Goal: |
| * Understand the inner working of aggregated authorization by stepping through |
| * access control management and permission evaluation. |
| * Note that this exercise uses a non-OSGi setup for training purpose relying on |
| * helpers available only for test setup. |
| * |
| * Exercises: |
| * Debug through each of the test methods in order to get an understand on how |
| * the composite setup works. |
| * |
| * - {@link #testTestUserReadPermissions()} |
| * - {@link #testTestUserWritePermissions()} |
| * - {@link #testGuestReadPermissions()} |
| * - {@link #testGuestWritePermissions()} |
| * - {@link #testAdminReadPermissions()} |
| * - {@link #testAdminWritePermissions()} |
| * - {@link #testEffectivePolicies()} |
| * - {@link #testApplicablePolicies()} |
| * - {@link #testGetPolicies()} |
| * - {@link #testRemovePolicy()} |
| * |
| * - Explain the {@link org.apache.jackrabbit.oak.spi.security.authorization.accesscontrol.PolicyOwner} interface. |
| * |
| * - Write more tests to explore aggregation of additional features within the |
| * authorization setup such as e.g. |
| * > Privilege discovery |
| * > Repository level privileges such as e.g. ability to register a new node type. |
| * > XML Import |
| * > Validation |
| * > Repository Initialization |
| * |
| * |
| * Advanced Exercises |
| * ----------------------------------------------------------------------------- |
| * |
| * - Change the 'authorizationCompositionType' to 'OR' and discuss the expected effect |
| * on the tests. Re-run the tests to verify your expectations. |
| * |
| * - Take another look at {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider}. |
| * What happens if a given {@link AuthorizationConfiguration} exposes a simple |
| * {@link org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider} |
| * that doesn't implement the aggregation-extension? |
| * To verify your findings write a test-setup that combines the default authorization model with |
| * {@link org.apache.jackrabbit.oak.exercise.security.authorization.models.predefined.PredefinedAuthorizationConfiguration} |
| * |
| * </pre> |
| */ |
| public class L3_UnderstandAggregationTest extends AbstractSecurityTest { |
| |
| private PropertyState prop; |
| |
| private AccessControlManager acMgr; |
| |
| @Override |
| public void before() throws Exception { |
| super.before(); |
| |
| prop = PropertyStates.createProperty("prop", "value"); |
| |
| Tree var = TreeUtil.addChild(root.getTree("/"), "var", NodeTypeConstants.NT_OAK_UNSTRUCTURED); |
| var.setProperty(prop); |
| |
| Tree content = TreeUtil.addChild(root.getTree("/"), "content", NodeTypeConstants.NT_OAK_UNSTRUCTURED); |
| content.setProperty(prop); |
| |
| Tree c1 = TreeUtil.addChild(content, "c1", NodeTypeConstants.NT_OAK_UNSTRUCTURED); |
| c1.setProperty(prop); |
| |
| Tree c2 = TreeUtil.addChild(content, "c2", NodeTypeConstants.NT_OAK_UNSTRUCTURED); |
| c2.setProperty(prop); |
| |
| acMgr = getAccessControlManager(root); |
| |
| // at /content grant read-access for everyone using default model |
| AccessControlList acl = AccessControlUtils.getAccessControlList(acMgr, content.getPath()); |
| acl.addAccessControlEntry(EveryonePrincipal.getInstance(), privilegesFromNames(PrivilegeConstants.JCR_READ)); |
| acMgr.setPolicy(content.getPath(), acl); |
| |
| // at /content/c1 deny reading properties (default model) and establish |
| // a CUG that limits access to test user |
| AccessControlPolicyIterator it = acMgr.getApplicablePolicies(c1.getPath()); |
| while (it.hasNext()) { |
| AccessControlPolicy policy = it.nextAccessControlPolicy(); |
| if (policy instanceof PrincipalSetPolicy) { |
| PrincipalSetPolicy psp = (PrincipalSetPolicy) policy; |
| psp.addPrincipals(getTestUser().getPrincipal()); |
| acMgr.setPolicy(c1.getPath(), psp); |
| } else if (policy instanceof JackrabbitAccessControlList) { |
| JackrabbitAccessControlList jacl = (JackrabbitAccessControlList) policy; |
| jacl.addEntry(EveryonePrincipal.getInstance(), privilegesFromNames(PrivilegeConstants.REP_ADD_PROPERTIES), true); |
| acMgr.setPolicy(c1.getPath(), jacl); |
| } |
| } |
| root.commit(); |
| } |
| |
| @Override |
| public void after() throws Exception { |
| try { |
| root.getTree("/content").remove(); |
| root.getTree("/var").remove(); |
| root.commit(); |
| } finally { |
| super.after(); |
| } |
| } |
| |
| @Override |
| protected SecurityProvider initSecurityProvider() { |
| SecurityProvider sp = super.initSecurityProvider(); |
| CugConfiguration cugConfiguration = new CugConfiguration(); |
| cugConfiguration.setParameters(ConfigurationParameters.of("cugSupportedPaths", new String[] {"/content"}, "cugEnabled", true)); |
| |
| SecurityProviderHelper.updateConfig(sp, cugConfiguration, AuthorizationConfiguration.class); |
| return sp; |
| } |
| |
| @Override |
| protected ConfigurationParameters getSecurityConfigParameters() { |
| return ConfigurationParameters.of("authorizationCompositionType", CompositeAuthorizationConfiguration.CompositionType.AND.toString()); |
| } |
| |
| @Test |
| public void testTestUserReadPermissions() throws Exception { |
| try (ContentSession cs = createTestSession()) { |
| Root r = cs.getLatestRoot(); |
| |
| assertFalse(r.getTree("/").exists()); |
| assertFalse(r.getTree("/var").exists()); |
| |
| Tree t = r.getTree("/content"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| |
| t = r.getTree("/content/c2"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| |
| t = r.getTree("/content/c1"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| } |
| } |
| |
| @Test |
| public void testTestUserWritePermissions() throws Exception { |
| try (ContentSession cs = createTestSession()) { |
| Root r = cs.getLatestRoot(); |
| |
| Tree t = r.getTree("/content/c1"); |
| t.setProperty("addingProperty", "value"); |
| root.commit(); |
| |
| assertFalse(getAccessControlManager(r).hasPrivileges("/content/c1", privilegesFromNames(PrivilegeConstants.JCR_ADD_CHILD_NODES))); |
| assertFalse(getAccessControlManager(r).hasPrivileges("/content/c1", privilegesFromNames(PrivilegeConstants.JCR_REMOVE_CHILD_NODES))); |
| assertFalse(getAccessControlManager(r).hasPrivileges("/content/c1", privilegesFromNames(PrivilegeConstants.REP_REMOVE_PROPERTIES))); |
| assertFalse(getAccessControlManager(r).hasPrivileges("/content/c1", privilegesFromNames(PrivilegeConstants.REP_ALTER_PROPERTIES))); |
| } |
| } |
| |
| @Test |
| public void testGuestReadPermissions() throws Exception { |
| try (ContentSession cs = login(new GuestCredentials())) { |
| Root r = cs.getLatestRoot(); |
| |
| assertFalse(r.getTree("/").exists()); |
| assertFalse(r.getTree("/var").exists()); |
| |
| Tree t = r.getTree("/content"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| |
| t = r.getTree("/content/c2"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| |
| assertFalse(r.getTree("/content/c1").exists()); |
| } |
| } |
| |
| @Test(expected = CommitFailedException.class) |
| public void testGuestWritePermissions() throws Exception { |
| try (ContentSession cs = login(new GuestCredentials())) { |
| Root r = cs.getLatestRoot(); |
| |
| // EXERCISE: write additional code testing permissions required for |
| // another property and child node. |
| |
| Tree t = r.getTree("/content"); |
| t.setProperty("prop2", "value"); |
| t.addChild("anotherChild"); |
| |
| r.commit(); |
| } |
| } |
| |
| @Test |
| public void testAdminReadPermissions() { |
| assertTrue(root.getTree("/").exists()); |
| assertTrue(root.getTree("/var").exists()); |
| |
| Tree t = root.getTree("/content"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| |
| t = root.getTree("/content/c2"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| |
| t = root.getTree("/content/c1"); |
| assertTrue(t.exists()); |
| assertTrue(t.hasProperty(prop.getName())); |
| } |
| |
| @Test |
| public void testAdminWritePermissions() throws Exception { |
| for (String p : new String[] {"/", "/var", "/content", "/content/c1", "/content/c2"}) { |
| assertTrue(acMgr.hasPrivileges(p, privilegesFromNames(PrivilegeConstants.JCR_ALL))); |
| } |
| } |
| |
| @Test |
| public void testEffectivePolicies() throws Exception { |
| // EXERCISE: inspect the effective policies to be discovered at the various paths |
| // explain the result and the differences. |
| |
| // EXERCISE: compare list of effective policies to policies return upon AccessControlManager.getPolicies |
| |
| AccessControlPolicy[] effective = acMgr.getEffectivePolicies("/content/c1"); |
| assertEquals(3, effective.length); |
| |
| effective = acMgr.getEffectivePolicies("/content/c2"); |
| assertEquals(1, effective.length); |
| |
| effective = acMgr.getEffectivePolicies("/var"); |
| assertEquals(0, effective.length); |
| |
| effective = acMgr.getEffectivePolicies(NamespaceConstants.NAMESPACES_PATH); |
| assertEquals(1, effective.length); |
| |
| // EXERCISE: try to modify the policies obtained in any of the calls |
| |
| // EXERCISE: try to write back the effective policies using AccessControlManager.setPolicy |
| } |
| |
| @Test |
| public void testApplicablePolicies() throws Exception { |
| // EXERCISE: observe the type of applicable policies and explain differences |
| // between the 3 paths. |
| AccessControlPolicyIterator it = acMgr.getApplicablePolicies("/content/c2"); |
| int cnt = 0; |
| while (it.hasNext()) { |
| AccessControlPolicy policy = it.nextAccessControlPolicy(); |
| cnt++; |
| } |
| assertEquals(2, cnt); |
| |
| it = acMgr.getApplicablePolicies("/var"); |
| cnt = 0; |
| while (it.hasNext()) { |
| AccessControlPolicy policy = it.nextAccessControlPolicy(); |
| cnt++; |
| } |
| assertEquals(1, cnt); |
| |
| it = acMgr.getApplicablePolicies("/content"); |
| cnt = 0; |
| while (it.hasNext()) { |
| AccessControlPolicy policy = it.nextAccessControlPolicy(); |
| cnt++; |
| } |
| assertEquals(1, cnt); |
| |
| // EXERCISE: expand the test by optionally modifying the applicable policies |
| // and writing them back using AccessControlManager.setPolicy |
| |
| // EXERCISE: explain the effective permissions resulting from both applying |
| // empty and modified policies. |
| } |
| |
| @Test |
| public void testGetSetPolicies() throws Exception { |
| AccessControlPolicy[] policies = acMgr.getPolicies("/content/c1"); |
| |
| assertEquals(2, policies.length); |
| |
| // EXERCISE: observe how the different authorization models claim responsibility for the setPolicy call. |
| for (AccessControlPolicy policy : policies) { |
| acMgr.setPolicy("/content/c1", policy); |
| } |
| |
| assertEquals(2, acMgr.getPolicies("/content/c1").length); |
| } |
| |
| @Test |
| public void testRemovePolicy() throws Exception { |
| AccessControlPolicy[] policies = acMgr.getPolicies("/content/c1"); |
| |
| assertEquals(2, policies.length); |
| |
| // EXERCISE: observe how the different authorization models claim responsibility for the removal. |
| for (AccessControlPolicy policy : policies) { |
| acMgr.removePolicy("/content/c1", policy); |
| } |
| |
| assertEquals(0, acMgr.getPolicies("/content/c1").length); |
| } |
| } |