Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java?rev=1800063&r1=1800062&r2=1800063&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderEmptyTest.java Tue Jun 27 14:11:33 2017 @@ -40,7 +40,7 @@ import static org.junit.Assert.assertFal import static org.junit.Assert.assertTrue; /** - * Test the effect of the combination of + * Test the effect of the 'AND' combination of * * - default permission provider, which always grants full access to an administrative session. * - custom provider that prevents all access but supports all permissions and @@ -51,15 +51,28 @@ import static org.junit.Assert.assertTru * * The expected outcome is that despite the default provider granting full access, * the combination effectively prevents any access. + * <p> + * Test the effect of the 'OR' combination of + * + * - default permission provider, which always grants full access to an administrative session. + * - custom provider that prevents all access but supports all permissions and + * thus is always called during composite evaluation. + * + * The tests are executed with the set of principals associated with the admin session, + * which in the default permission provider is granted full access. + * + * The expected outcome is that the combination effectively allows any access. */ public class CompositeProviderEmptyTest extends AbstractCompositeProviderTest { private CompositePermissionProvider cpp; + private CompositePermissionProvider cppO; @Override public void before() throws Exception { super.before(); cpp = createPermissionProvider(root.getContentSession().getAuthInfo().getPrincipals()); + cppO = createPermissionProviderOR(root.getContentSession().getAuthInfo().getPrincipals()); } @Override @@ -71,12 +84,14 @@ public class CompositeProviderEmptyTest public void testGetPrivileges() throws Exception { for (String p : NODE_PATHS) { assertTrue(cpp.getPrivileges(readOnlyRoot.getTree(p)).isEmpty()); + assertTrue(cppO.getPrivileges(readOnlyRoot.getTree(p)).isEmpty()); } } @Test public void testGetPrivilegesOnRepo() throws Exception { assertTrue(cpp.getPrivileges(null).isEmpty()); + assertTrue(cppO.getPrivileges(null).isEmpty()); } @Test @@ -92,10 +107,10 @@ public class CompositeProviderEmptyTest @Test public void testHasPrivilegesOnRepo() throws Exception { - assertFalse(cpp.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT)); + assertFalse(cpp.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT)); + assertTrue(cppO.hasPrivileges(null, JCR_NAMESPACE_MANAGEMENT, JCR_NODE_TYPE_DEFINITION_MANAGEMENT)); } - @Test public void testIsGranted() throws Exception { for (String p : NODE_PATHS) { @@ -104,6 +119,9 @@ public class CompositeProviderEmptyTest assertFalse(cpp.isGranted(tree, null, Permissions.READ_NODE)); assertFalse(cpp.isGranted(tree, null, Permissions.READ_NODE | Permissions.MODIFY_CHILD_NODE_COLLECTION)); assertFalse(cpp.isGranted(tree, null, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); + assertTrue(cppO.isGranted(tree, null, Permissions.READ_NODE)); + assertTrue(cppO.isGranted(tree, null, Permissions.READ_NODE | Permissions.MODIFY_CHILD_NODE_COLLECTION)); + assertTrue(cppO.isGranted(tree, null, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); } } @@ -117,6 +135,11 @@ public class CompositeProviderEmptyTest assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.ADD_PROPERTY)); assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.REMOVE_PROPERTY)); assertFalse(cpp.isGranted(tree, PROPERTY_STATE, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.MODIFY_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.ADD_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.REMOVE_PROPERTY)); + assertTrue(cppO.isGranted(tree, PROPERTY_STATE, Permissions.READ_ACCESS_CONTROL | Permissions.MODIFY_ACCESS_CONTROL)); } } @@ -128,10 +151,14 @@ public class CompositeProviderEmptyTest assertFalse(cpp.isGranted(nodePath, Session.ACTION_REMOVE)); assertFalse(cpp.isGranted(propPath, JackrabbitSession.ACTION_MODIFY_PROPERTY)); - assertFalse(cpp.isGranted(nodePath, getActionString(JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL, JackrabbitSession.ACTION_READ_ACCESS_CONTROL))); assertFalse(cpp.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY)); assertFalse(cpp.isGranted(nonExisting, Session.ACTION_ADD_NODE)); + assertTrue(cppO.isGranted(nodePath, Session.ACTION_REMOVE)); + assertTrue(cppO.isGranted(propPath, JackrabbitSession.ACTION_MODIFY_PROPERTY)); + assertTrue(cppO.isGranted(nodePath, getActionString(JackrabbitSession.ACTION_MODIFY_ACCESS_CONTROL, JackrabbitSession.ACTION_READ_ACCESS_CONTROL))); + assertTrue(cppO.isGranted(nonExisting, JackrabbitSession.ACTION_ADD_PROPERTY)); + assertTrue(cppO.isGranted(nonExisting, Session.ACTION_ADD_NODE)); } } @@ -140,20 +167,31 @@ public class CompositeProviderEmptyTest RepositoryPermission rp = cpp.getRepositoryPermission(); assertFalse(rp.isGranted(Permissions.NAMESPACE_MANAGEMENT)); assertFalse(rp.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT)); + RepositoryPermission rpO = cppO.getRepositoryPermission(); + assertTrue(rpO.isGranted(Permissions.NAMESPACE_MANAGEMENT)); + assertTrue(rpO.isGranted(Permissions.NODE_TYPE_DEFINITION_MANAGEMENT)); } @Test public void testTreePermissionIsGranted() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { TreePermission tp = cpp.getTreePermission(readOnlyRoot.getTree(path), parentPermission); - assertFalse(tp.isGranted(Permissions.READ_NODE)); assertFalse(tp.isGranted(Permissions.REMOVE_NODE)); assertFalse(tp.isGranted(Permissions.ALL)); + parentPermission = tp; + } + } - + @Test + public void testTreePermissionIsGrantedOR() throws Exception { + TreePermission parentPermission = TreePermission.EMPTY; + for (String path : TP_PATHS) { + TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission); + assertTrue(tp.isGranted(Permissions.READ_NODE)); + assertTrue(tp.isGranted(Permissions.REMOVE_NODE)); + assertTrue(tp.isGranted(Permissions.ALL)); parentPermission = tp; } } @@ -161,13 +199,21 @@ public class CompositeProviderEmptyTest @Test public void testTreePermissionIsGrantedProperty() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { TreePermission tp = cpp.getTreePermission(readOnlyRoot.getTree(path), parentPermission); - assertFalse(tp.isGranted(Permissions.READ_PROPERTY, PROPERTY_STATE)); assertFalse(tp.isGranted(Permissions.REMOVE_PROPERTY, PROPERTY_STATE)); + parentPermission = tp; + } + } + @Test + public void testTreePermissionIsGrantedPropertyOR() throws Exception { + TreePermission parentPermission = TreePermission.EMPTY; + for (String path : TP_PATHS) { + TreePermission tp = cppO.getTreePermission(readOnlyRoot.getTree(path), parentPermission); + assertTrue(tp.isGranted(Permissions.READ_PROPERTY, PROPERTY_STATE)); + assertTrue(tp.isGranted(Permissions.REMOVE_PROPERTY, PROPERTY_STATE)); parentPermission = tp; } } @@ -175,12 +221,21 @@ public class CompositeProviderEmptyTest @Test public void testTreePermissionCanRead() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { Tree t = readOnlyRoot.getTree(path); TreePermission tp = cpp.getTreePermission(t, parentPermission); assertFalse(tp.canRead()); + parentPermission = tp; + } + } + @Test + public void testTreePermissionCanReadOR() throws Exception { + TreePermission parentPermission = TreePermission.EMPTY; + for (String path : TP_PATHS) { + Tree t = readOnlyRoot.getTree(path); + TreePermission tp = cppO.getTreePermission(t, parentPermission); + assertTrue(tp.canRead()); parentPermission = tp; } } @@ -188,12 +243,21 @@ public class CompositeProviderEmptyTest @Test public void testTreePermissionCanReadProperty() throws Exception { TreePermission parentPermission = TreePermission.EMPTY; - for (String path : TP_PATHS) { Tree t = readOnlyRoot.getTree(path); TreePermission tp = cpp.getTreePermission(t, parentPermission); assertFalse(tp.canRead(PROPERTY_STATE)); + parentPermission = tp; + } + } + @Test + public void testTreePermissionCanReadPropertyOR() throws Exception { + TreePermission parentPermission = TreePermission.EMPTY; + for (String path : TP_PATHS) { + Tree t = readOnlyRoot.getTree(path); + TreePermission tp = cppO.getTreePermission(t, parentPermission); + assertTrue(tp.canRead(PROPERTY_STATE)); parentPermission = tp; } } @@ -201,11 +265,11 @@ public class CompositeProviderEmptyTest /** * {@code AggregatedPermissionProvider} that doesn't grant any access. */ - private static final class EmptyAggregatedProvider extends AbstractAggrProvider { + static class EmptyAggregatedProvider extends AbstractAggrProvider { private static final PermissionProvider BASE = EmptyPermissionProvider.getInstance(); - private EmptyAggregatedProvider(@Nonnull Root root) { + public EmptyAggregatedProvider(@Nonnull Root root) { super(root); }
Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java?rev=1800063&r1=1800062&r2=1800063&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeProviderNoScopeTest.java Tue Jun 27 14:11:33 2017 @@ -93,6 +93,20 @@ public class CompositeProviderNoScopeTes @Override @Test public void testGetTreePermissionInstance() throws Exception { + PermissionProvider pp = createPermissionProviderOR(); + TreePermission parentPermission = TreePermission.EMPTY; + + for (String path : TP_PATHS) { + Tree t = readOnlyRoot.getTree(path); + TreePermission tp = pp.getTreePermission(t, parentPermission); + assertCompositeTreePermission(t.isRoot(), tp); + parentPermission = tp; + } + } + + @Override + @Test + public void testGetTreePermissionInstanceOR() throws Exception { PermissionProvider pp = createPermissionProvider(); TreePermission parentPermission = TreePermission.EMPTY; @@ -115,6 +129,23 @@ public class CompositeProviderNoScopeTes assertCompositeTreePermission(tp); for (String cName : childNames) { + ns = ns.getChildNode(cName); + tp = tp.getChildPermission(cName, ns); + assertCompositeTreePermission(false, tp); + } + } + + @Override + @Test + public void testTreePermissionGetChildOR() throws Exception { + List<String> childNames = ImmutableList.of("test", "a", "b", "c", "nonexisting"); + + Tree rootTree = readOnlyRoot.getTree(ROOT_PATH); + NodeState ns = ((ImmutableTree) rootTree).getNodeState(); + TreePermission tp = createPermissionProviderOR().getTreePermission(rootTree, TreePermission.EMPTY); + assertCompositeTreePermission(tp); + + for (String cName : childNames) { ns = ns.getChildNode(cName); tp = tp.getChildPermission(cName, ns); assertCompositeTreePermission(false, tp); Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java?rev=1800063&r1=1800062&r2=1800063&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authorization/composite/CompositeTreePermissionTest.java Tue Jun 27 14:11:33 2017 @@ -23,14 +23,16 @@ import javax.annotation.Nonnull; import org.apache.jackrabbit.oak.AbstractSecurityTest; 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.plugins.tree.RootFactory; +import org.apache.jackrabbit.oak.plugins.tree.TreeUtil; import org.apache.jackrabbit.oak.plugins.tree.impl.ImmutableTree; +import org.apache.jackrabbit.oak.security.authorization.composite.CompositeAuthorizationConfiguration.CompositionType; import org.apache.jackrabbit.oak.spi.security.Context; import org.apache.jackrabbit.oak.spi.security.authorization.permission.AggregatedPermissionProvider; import org.apache.jackrabbit.oak.spi.security.authorization.permission.TreePermission; import org.apache.jackrabbit.oak.spi.state.NodeState; -import org.apache.jackrabbit.oak.util.NodeUtil; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -50,8 +52,8 @@ public class CompositeTreePermissionTest public void before() throws Exception { super.before(); - NodeUtil rootNode = new NodeUtil(root.getTree("/")); - rootNode.addChild("test", NodeTypeConstants.NT_OAK_UNSTRUCTURED); + Tree rootNode = root.getTree("/"); + TreeUtil.addChild(rootNode, "test", NodeTypeConstants.NT_OAK_UNSTRUCTURED); root.commit(); readOnlyRoot = RootFactory.createReadOnlyRoot(root); @@ -72,7 +74,8 @@ public class CompositeTreePermissionTest } private TreePermission createRootTreePermission(AggregatedPermissionProvider... providers) { - return new CompositePermissionProvider(readOnlyRoot, Arrays.asList(providers), Context.DEFAULT).getTreePermission(rootTree, TreePermission.EMPTY); + return new CompositePermissionProvider(readOnlyRoot, Arrays.asList(providers), Context.DEFAULT, CompositionType.AND) + .getTreePermission(rootTree, TreePermission.EMPTY); } private static void assertCompositeTreePermission(boolean expected, @Nonnull TreePermission tp) { Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authorization/composite.md URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authorization/composite.md?rev=1800063&r1=1800062&r2=1800063&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authorization/composite.md (original) +++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authorization/composite.md Tue Jun 27 14:11:33 2017 @@ -87,7 +87,7 @@ Once multiple modules are deployed a [Co characteristics will be returned: - API calls reading information will return the combined result of the wrapped implementations. -- Methods defined solely by `JackrabbitAccessControlManager` additionally test for the delegatees to implement that extentions. +- Methods defined solely by `JackrabbitAccessControlManager` additionally test for the delegatees to implement that extension. - API calls writing back policies will look for the responsible `PolicyOwner` and specifically delegate the call. If no owner can be found an `AccessControlException` is thrown. Hence, a given authorization model is free to implement JCR `AccessControlManager` @@ -115,7 +115,7 @@ effective permissions: permissions have been successfully processed and none of the delegatees involved denied access. -This implies that evalution of permissions across multiple implementations is +This implies that evaluation of permissions across multiple implementations is strictly additive: as soon as one provider denies access (either by an explicit deny or by a missing explicit allow) permissions are denied. @@ -127,12 +127,12 @@ For a given permission provider this mea the context of the aggregation (i.e. single model setup), a 'limited' provider must never grant access for permissions or items it isn't able to handle properly. In other words: permissions that have not been explicitly granted within the scope -of an implemenation must be denied. +of an implementation must be denied. #### Restriction Management Support for multiple restriction providers has already been been present with the -default authorization implementation since Oak 1.0. The mechnism described in +default authorization implementation since Oak 1.0. The mechanism described in section [Restriction Management](restriction.html) is not affected by the new functionality. The `CompositeAuthorizationConfiguration` is in charge of collecting @@ -148,8 +148,8 @@ types of `AccessControlPolicy` where res <a name="configuration"/> ### Configuration -There are no implementation specific configuration options associated with -the `CompositeAuthorizationConfiguration`. +By default the `CompositeAuthorizationConfiguration` aggregates results by applying an `AND` operation to the current set of providers. +This can be changed via configuration to an `OR`. See section [Introduction to Oak Security](../../introduction.html#configuration) for further details. <a name="pluggability"/> ### Pluggability @@ -158,7 +158,7 @@ The following steps are required to plug the Oak repository: - Implement your custom `AuthorizationConfiguration` -- Deploy the bundle containing the implemenation +- Deploy the bundle containing the implementation - Bind your `AuthorizationConfiguration` to the `SecurityProvider`: - in an OSGi setup this is achieved by adding the configuration to the `requiredServicePids` property of the `SecurityProviderRegistration` _("Apache Jackrabbit Oak SecurityProvider")_ Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/introduction.md URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/introduction.md?rev=1800063&r1=1800062&r2=1800063&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/introduction.md (original) +++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/introduction.md Tue Jun 27 14:11:33 2017 @@ -73,7 +73,7 @@ successfully been resolved. This new app the initial security provider implementation and has been backported to existing branches (see [OAK-3201] and [OAK-3441]). -While optional configuration settting can be changed or extended at runtime, +While optional configuration setting can be changed or extended at runtime, modules and extensions considered required for a functional security setup, need to be listed in the _"Required Service PIDs"_ property. This asserts both reliable security setup and proper initialization of the individual modules. See also @@ -126,7 +126,7 @@ only authentication and authorization ar This is compliant with the security requirements defined by JSR 283 which defines API to login into the repository and mandates minimal permission evaluation, -be it implemenation specific of imposed by the optional access control management. +be it implementation specific of imposed by the optional access control management. The minimal security setup may consequently be reduced to a setup as defined by the following imaginary, custom `SecurityProvider` (see also [OpenSecurityProvider]) @@ -167,7 +167,7 @@ of view. Please note the following depen 1. **Authentication** is mandatory and expected to bind a set of `Principal`s to the `Subject`. This may happen before or during the repository login. -2. **Permission Evalution** is mandatory and associated with the set of `Principal`s +2. **Permission Evaluation** is mandatory and associated with the set of `Principal`s bound to to the `Subject` during the authentication step. 3. `Principal`s represent the link between authentication and authorization and _MAY_ be exposed by Principal Management module as described above. @@ -178,7 +178,7 @@ of view. Please note the following depen along with exposing the corresponding principals as part of the Principal Management. 6. **User Management** is optional and _MAY_ be used for credentials validation during the authentication step. If present it is _usually_ used as a source for principals exposed by Principal Management. - + <a name="configuration"/> ### Configuration @@ -208,6 +208,12 @@ The value of this configuration paramete module or functionality that is considered required for a successful security setup. See section [pluggability](#pluggability) below. +| Parameter | Type | Default | Description | +|--------------------------|----------|-----------|------------------------| +| `Authorization Composition Type` | String (AND|OR) | AND | The Composite Authorization model uses this flag to determine what type of logic to apply to the existing providers| + +Given a set of permission providers, the composite model can aggregate the results by applying an `AND` logic (for example all providers must allow a specific privilege in order to be granted), or an `OR` (for example any provider can allow a privilege). By default the `AND` version is used. + #### CompositeConfiguration | Parameter | Type | Default | Description |
