Index: oak-authorization-cug/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugConfiguration.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-authorization-cug/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugConfiguration.java (revision 1827461) +++ oak-authorization-cug/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugConfiguration.java (revision ) @@ -100,7 +100,7 @@ /** * Reference to services implementing {@link org.apache.jackrabbit.oak.spi.security.authorization.cug.CugExclude}. */ - @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY) + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) private CugExclude exclude; /** @@ -215,6 +215,14 @@ // set to null (and not default) to comply with OSGi lifecycle, // if the reference is unset it means the service is being deactivated this.mountInfoProvider = null; + } + + public void bindExclude(CugExclude exclude) { + this.exclude = exclude; + } + + public void unbindExclude(CugExclude exclude) { + this.exclude = null; } //-------------------------------------------------------------------------- \ No newline at end of file Index: oak-doc/src/site/markdown/security/authorization/cug.md IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-doc/src/site/markdown/security/authorization/cug.md (revision 1827461) +++ oak-doc/src/site/markdown/security/authorization/cug.md (revision ) @@ -233,7 +233,7 @@ | `principalNames` | Set\ | \- | Name of principals that are always excluded from CUG evaluation. | | | | | | -_Note:_ this is an optional feature to extend the [default](/oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authorization/cug/CugExclude.Default.html) +_Note:_ This implementation extends the [default](/oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authorization/cug/CugExclude.Default.html) exclusion list. Alternatively, it is possible to plug a custom `CugExclude` implementation matching specific needs (see [below](#pluggability)). @@ -296,7 +296,8 @@ 1. implement `CugExclude` interface according to you needs, 2. make your implementation an OSGi service -3. deploy the bundle containing your implementation in the OSGi container and activate the service. +3. deploy the bundle containing your implementation in the OSGi container and activate the service. +4. make sure the default CUGExclude service is properly replaced by the custom implementation. ###### Example \ No newline at end of file Index: oak-authorization-cug/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugExcludeImpl.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-authorization-cug/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugExcludeImpl.java (revision 1827461) +++ oak-authorization-cug/src/main/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugExcludeImpl.java (revision ) @@ -25,7 +25,6 @@ import com.google.common.collect.ImmutableSet; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.ConfigurationPolicy; import org.apache.felix.scr.annotations.Modified; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; @@ -37,19 +36,18 @@ * Extension of the default {@link org.apache.jackrabbit.oak.spi.security.authorization.cug.CugExclude} * implementation that allow to specify additional principal names to be excluded * from CUG evaluation. - * - * Note: this component is requires a configuration (i.e. a configured list of - * principal names) in order to be activated. */ @Component(metatype = true, + immediate = true, label = "Apache Jackrabbit Oak CUG Exclude List", - description = "Allows to exclude principal(s) with the configured name(s) from CUG evaluation.", - policy = ConfigurationPolicy.REQUIRE) + description = "Exclude principal(s) from CUG evaluation. In addition to the " + + "principals defined by the default CugExclude ('AdminPrincipal', 'SystemPrincipal', 'SystemUserPrincipal' classes), " + + "this component allows to optionally configure additional principals by name.") @Service({CugExclude.class}) @Properties({ @Property(name = "principalNames", label = "Principal Names", - description = "Name of principals that are always excluded from CUG evaluation.", + description = "Name(s) of additional principal(s) that are excluded from CUG evaluation.", cardinality = Integer.MAX_VALUE) }) public class CugExcludeImpl extends CugExclude.Default { \ No newline at end of file Index: oak-authorization-cug/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugConfigurationOsgiTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-authorization-cug/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugConfigurationOsgiTest.java (revision ) +++ oak-authorization-cug/src/test/java/org/apache/jackrabbit/oak/spi/security/authorization/cug/impl/CugConfigurationOsgiTest.java (revision ) @@ -0,0 +1,144 @@ +/* + * 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.spi.security.authorization.cug.impl; + +import java.security.Principal; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.apache.jackrabbit.oak.AbstractSecurityTest; +import org.apache.jackrabbit.oak.composite.MountInfoProviderService; +import org.apache.jackrabbit.oak.plugins.tree.impl.RootProviderService; +import org.apache.jackrabbit.oak.plugins.tree.impl.TreeProviderService; +import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.EmptyPermissionProvider; +import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionProvider; +import org.apache.jackrabbit.oak.spi.security.principal.AdminPrincipal; +import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl; +import org.apache.jackrabbit.oak.spi.security.principal.SystemPrincipal; +import org.apache.jackrabbit.oak.spi.security.principal.SystemUserPrincipal; +import org.apache.sling.testing.mock.osgi.ReferenceViolationException; +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class CugConfigurationOsgiTest extends AbstractSecurityTest { + + private static final String EXCLUDED_PRINCIPAL_NAME = "excludedPrincipal"; + private static final String ANY_PRINCIPAL_NAME = "anyPrincipal"; + + private static final Map PROPERTIES = ImmutableMap.of( + CugConstants.PARAM_CUG_ENABLED, true, + CugConstants.PARAM_CUG_SUPPORTED_PATHS, new String[] {"/"}); + + @Rule + public final OsgiContext context = new OsgiContext(); + + private CugConfiguration cugConfiguration; + private CugExcludeImpl cugExclude; + private String wspName; + + @Before + public void before() throws Exception { + super.before(); + + wspName = root.getContentSession().getWorkspaceName(); + + cugConfiguration = new CugConfiguration(getSecurityProvider()); + cugConfiguration.setRootProvider(new RootProviderService()); + cugConfiguration.setTreeProvider(new TreeProviderService()); + + cugExclude = new CugExcludeImpl(); + + MountInfoProviderService mip = new MountInfoProviderService(); + context.registerInjectActivateService(mip); + } + + @Test(expected = ReferenceViolationException.class) + public void testMissingCugExclude() { + context.registerInjectActivateService(cugConfiguration, PROPERTIES); + } + + @Test + public void testCugExcludeExcludedDefault() { + context.registerInjectActivateService(cugExclude); + context.registerInjectActivateService(cugConfiguration, PROPERTIES); + + // default exclusion + AdminPrincipal admin = () -> "name"; + SystemUserPrincipal suPrincipal = () -> "name"; + + AuthorizationConfiguration config = context.getService(AuthorizationConfiguration.class); + for (Principal p : new Principal[] {SystemPrincipal.INSTANCE, admin, suPrincipal}) { + PermissionProvider permissionProvider = config.getPermissionProvider(root, wspName, ImmutableSet.of(p)); + assertSame(EmptyPermissionProvider.getInstance(), permissionProvider); + } + + // however, other principals must not be excluded + PermissionProvider permissionProvider = config.getPermissionProvider(root, wspName, ImmutableSet.of(new PrincipalImpl(EXCLUDED_PRINCIPAL_NAME))); + assertTrue(permissionProvider instanceof CugPermissionProvider); + } + + @Test + public void testCugExcludeExcludedPrincipal() { + context.registerInjectActivateService(cugExclude, ImmutableMap.of("principalNames", new String[] {EXCLUDED_PRINCIPAL_NAME})); + context.registerInjectActivateService(cugConfiguration, PROPERTIES); + + AuthorizationConfiguration config = context.getService(AuthorizationConfiguration.class); + PermissionProvider permissionProvider = config.getPermissionProvider(root, wspName, ImmutableSet.of(new PrincipalImpl(EXCLUDED_PRINCIPAL_NAME))); + assertSame(EmptyPermissionProvider.getInstance(), permissionProvider); + } + + @Test + public void testCugExcludeAnyPrincipal() { + context.registerInjectActivateService(cugExclude, ImmutableMap.of("principalNames", new String[] {EXCLUDED_PRINCIPAL_NAME})); + context.registerInjectActivateService(cugConfiguration, PROPERTIES); + + AuthorizationConfiguration config = context.getService(AuthorizationConfiguration.class); + PermissionProvider permissionProvider = config.getPermissionProvider(root, wspName, ImmutableSet.of(new PrincipalImpl(ANY_PRINCIPAL_NAME))); + assertTrue(permissionProvider instanceof CugPermissionProvider); + } + + @Test + public void testNotEnabled() { + context.registerInjectActivateService(cugExclude, ImmutableMap.of("principalNames", new String[] {ANY_PRINCIPAL_NAME})); + context.registerInjectActivateService(cugConfiguration, ImmutableMap.of( + CugConstants.PARAM_CUG_ENABLED, false, + CugConstants.PARAM_CUG_SUPPORTED_PATHS, new String[]{"/"})); + + AuthorizationConfiguration config = context.getService(AuthorizationConfiguration.class); + PermissionProvider permissionProvider = config.getPermissionProvider(root, wspName, ImmutableSet.of(new PrincipalImpl(ANY_PRINCIPAL_NAME))); + assertSame(EmptyPermissionProvider.getInstance(), permissionProvider); + } + + @Test + public void testNoSupportedPaths() { + context.registerInjectActivateService(cugExclude, ImmutableMap.of("principalNames", new String[] {ANY_PRINCIPAL_NAME})); + context.registerInjectActivateService(cugConfiguration, ImmutableMap.of( + CugConstants.PARAM_CUG_ENABLED, true, + CugConstants.PARAM_CUG_SUPPORTED_PATHS, new String[0])); + + AuthorizationConfiguration config = context.getService(AuthorizationConfiguration.class); + PermissionProvider permissionProvider = config.getPermissionProvider(root, wspName, ImmutableSet.of(new PrincipalImpl(ANY_PRINCIPAL_NAME))); + assertSame(EmptyPermissionProvider.getInstance(), permissionProvider); + } +} \ No newline at end of file Index: oak-authorization-cug/pom.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-authorization-cug/pom.xml (revision 1827461) +++ oak-authorization-cug/pom.xml (revision ) @@ -155,6 +155,12 @@ org.apache.jackrabbit + oak-store-composite + ${project.version} + test + + + org.apache.jackrabbit oak-jcr ${project.version} tests @@ -164,6 +170,11 @@ org.mockito mockito-core 1.10.19 + test + + + org.apache.sling + org.apache.sling.testing.osgi-mock test \ No newline at end of file