SLING-6285 - Implement LoginAdminWhitelist in JCR Base
- move implementation from o.a.s.jcr.oak.server to o.a.s.jcr.base
- move and refactor tests to work with moved implementation
- support @Modified callback on LoginAdminWhitelist to avoid restarting repositories on configuratin change
- improved error handling, i.e. set a message for LoginExceptions
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1769962 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index a99a2a8..4c8ae29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -177,7 +177,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.jcr.base</artifactId>
- <version>2.4.2</version>
+ <version>2.4.3-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/src/main/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelist.java b/src/main/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelist.java
deleted file mode 100644
index 2ed8fe9..0000000
--- a/src/main/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelist.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.sling.jcr.oak.server.internal;
-
-import java.util.Arrays;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import org.apache.sling.jcr.api.SlingRepository;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.metatype.annotations.Designate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Whitelist that defines which bundles can use the
- * {@link SlingRepository#loginAdministrative} method.
- *
- * The default configuration lets a few trusted Sling bundles
- * use the loginAdministrative method.
- */
-@Component(
- service = LoginAdminWhitelist.class,
- property = {
- Constants.SERVICE_DESCRIPTION + "=Apache Sling Login Admin Whitelist",
- Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
- }
-)
-@Designate(
- ocd = LoginAdminWhitelistConfiguration.class
-)
-public class LoginAdminWhitelist {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- private boolean bypassWhitelist;
-
- private Pattern whitelistRegexp;
-
- private Set<String> whitelistedBsn;
-
- @Activate
- void activate(LoginAdminWhitelistConfiguration config) {
- whitelistedBsn = new TreeSet<String>();
-
- if (config.whitelist_bundles_default() != null) {
- whitelistedBsn.addAll(Arrays.asList(config.whitelist_bundles_default()));
- }
- if (config.whitelist_bundles_additional() != null) { // null check due to FELIX-5404
- whitelistedBsn.addAll(Arrays.asList(config.whitelist_bundles_additional()));
- }
-
- final String regexp = config.whitelist_bundles_regexp();
- if(regexp.trim().length() > 0) {
- whitelistRegexp = Pattern.compile(regexp);
- log.warn("A whitelist.bundles.regexp is configured, this is NOT RECOMMENDED for production: {}", whitelistRegexp);
- } else {
- whitelistRegexp = null;
- }
-
- bypassWhitelist = config.whitelist_bypass();
- if(bypassWhitelist) {
- log.info("bypassWhitelist=true, whitelisted BSNs=<ALL>");
- log.warn(
- "All bundles are allowed to use loginAdministrative due to the 'bypass whitelist' configuration"
- + " of this service. This is NOT RECOMMENDED, for security reasons."
- );
- } else {
- log.info("bypassWhitelist=false, whitelisted BSNs({})={}", whitelistedBsn.size(), whitelistedBsn);
- }
- }
-
- boolean allowLoginAdministrative(Bundle b) {
- if(bypassWhitelist) {
- log.debug("Whitelist is bypassed, all bundles allowed to use loginAdministrative");
- return true;
- }
-
- final String bsn = b.getSymbolicName();
- if(whitelistRegexp != null && whitelistRegexp.matcher(bsn).matches()) {
- log.debug("{} is whitelisted to use loginAdministrative, by regexp", bsn);
- return true;
- } else if(whitelistedBsn.contains(bsn)) {
- log.debug("{} is whitelisted to use loginAdministrative, by explicit whitelist", bsn);
- return true;
- }
- log.debug("{} is not whitelisted to use loginAdministrative", bsn);
- return false;
- }
-}
diff --git a/src/main/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelistConfiguration.java b/src/main/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelistConfiguration.java
deleted file mode 100644
index 8810e35..0000000
--- a/src/main/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelistConfiguration.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.sling.jcr.oak.server.internal;
-
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@ObjectClassDefinition(
- name = "Apache Sling Login Admin Whitelist",
- description = "Defines which bundles can use SlingRepository.loginAdministrative()"
-)
-@interface LoginAdminWhitelistConfiguration {
-
- /** Need to allow for bypassing the whitelist, for backwards
- * compatibility with previous Sling versions which didn't
- * implement it. Setting this to true is not recommended
- * and logged as a warning.
- */
- @AttributeDefinition(
- name = "Bypass the whitelist",
- description = "Allow all bundles to use loginAdministrative(). Should ONLY be used " +
- "for backwards compatibility reasons and if you are aware of " +
- "the related security risks."
- )
- boolean whitelist_bypass() default false;
-
- @AttributeDefinition(
- name = "Whitelist regexp",
- description = "Regular expression for bundle symbolic names for which loginAdministrative() " +
- "is allowed. NOT recommended for production use, but useful for testing with " +
- "generated bundles."
- )
- String whitelist_bundles_regexp() default "";
-
- @AttributeDefinition(
- name = "Default whitelisted BSNs",
- description = "Default list of bundle symbolic names for which loginAdministrative() is allowed."
- )
- String[] whitelist_bundles_default() default {
- // TODO: remove bundles as their dependency on admin login is fixed, see SLING-5355 for linked issues
- "org.apache.sling.discovery.commons",
- "org.apache.sling.discovery.base",
- "org.apache.sling.discovery.oak",
- "org.apache.sling.extensions.webconsolesecurityprovider",
- "org.apache.sling.i18n",
- "org.apache.sling.installer.provider.jcr",
- "org.apache.sling.jcr.base",
- "org.apache.sling.jcr.contentloader",
- "org.apache.sling.jcr.davex",
- "org.apache.sling.jcr.jackrabbit.usermanager",
- "org.apache.sling.jcr.oak.server",
- "org.apache.sling.jcr.repoinit",
- "org.apache.sling.jcr.resource",
- "org.apache.sling.jcr.webconsole",
- "org.apache.sling.resourceresolver",
- "org.apache.sling.servlets.post", // remove when 2.3.16 is released
- "org.apache.sling.servlets.resolver"
- };
-
- @AttributeDefinition(
- name = "Additional whitelisted BSNs",
- description = "Additional list of bundle symbolic names for which loginAdministrative() is allowed."
- )
- String[] whitelist_bundles_additional() default {};
-}
diff --git a/src/main/java/org/apache/sling/jcr/oak/server/internal/OakSlingRepositoryManager.java b/src/main/java/org/apache/sling/jcr/oak/server/internal/OakSlingRepositoryManager.java
index 5292d8b..b042dae 100644
--- a/src/main/java/org/apache/sling/jcr/oak/server/internal/OakSlingRepositoryManager.java
+++ b/src/main/java/org/apache/sling/jcr/oak/server/internal/OakSlingRepositoryManager.java
@@ -99,9 +99,6 @@
@Reference
private ThreadPoolManager threadPoolManager = null;
- @Reference
- private LoginAdminWhitelist loginAdminWhitelist;
-
private ThreadPool threadPool;
private ServiceRegistration oakExecutorServiceReference;
@@ -122,9 +119,6 @@
private ServiceRegistration nodeAggregatorRegistration;
- public OakSlingRepositoryManager() {
- }
-
@Override
protected ServiceUserMapper getServiceUserMapper() {
return this.serviceUserMapper;
@@ -203,11 +197,6 @@
((JackrabbitRepository) repository).shutdown();
}
- @Override
- protected boolean allowLoginAdministrativeForBundle(final Bundle bundle) {
- return loginAdminWhitelist.allowLoginAdministrative(bundle);
- }
-
@Activate
private void activate(final OakSlingRepositoryManagerConfiguration configuration, final ComponentContext componentContext) {
this.configuration = configuration;
diff --git a/src/test/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelistTest.java b/src/test/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelistTest.java
deleted file mode 100644
index 527dd9c..0000000
--- a/src/test/java/org/apache/sling/jcr/oak/server/internal/LoginAdminWhitelistTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.sling.jcr.oak.server.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.osgi.framework.Bundle;
-
-public class LoginAdminWhitelistTest {
-
- private static final String TYPICAL_DEFAULT_ALLOWED_BSN = "org.apache.sling.jcr.base";
-
- private LoginAdminWhitelist whitelist;
-
- @Before
- public void setup() {
- whitelist = new LoginAdminWhitelist();
- }
-
- private void assertAdminLogin(final String bundleSymbolicName, boolean expected) {
- final Bundle b = Mockito.mock(Bundle.class);
- when(b.getSymbolicName()).thenReturn(bundleSymbolicName);
- final boolean actual = whitelist.allowLoginAdministrative(b);
- assertEquals("For bundle " + bundleSymbolicName + ", expected admin login=" + expected, expected, actual);
- }
-
- private List<String> randomBsn() {
- final List<String> result = new ArrayList<String>();
- for(int i=0; i < 5; i++) {
- result.add("random.bsn." + UUID.randomUUID());
- }
- return result;
- }
-
- @Test
- public void testDefaultConfig() {
- final LoginAdminWhitelistConfiguration config = config(null, null, null, null);
- whitelist.activate(config);
-
- for(String bsn : config.whitelist_bundles_default()) {
- assertAdminLogin(bsn, true);
- }
-
- assertAdminLogin(TYPICAL_DEFAULT_ALLOWED_BSN, true);
-
- for(String bsn : randomBsn()) {
- assertAdminLogin(bsn, false);
- }
- }
-
- @Test
- public void testBypassWhitelist() {
- whitelist.activate(config(true, null, null, null));
-
- for(String bsn : randomBsn()) {
- assertAdminLogin(bsn, true);
- }
- }
-
- @Test
- public void testDefaultConfigOnly() {
- final String [] allowed = {
- "bundle1", "bundle2"
- };
- whitelist.activate(config(null, null, allowed, null));
-
- assertAdminLogin("bundle1", true);
- assertAdminLogin("bundle2", true);
- assertAdminLogin("foo.1.bar", false);
- assertAdminLogin(TYPICAL_DEFAULT_ALLOWED_BSN, false);
-
- for(String bsn : randomBsn()) {
- assertAdminLogin(bsn, false);
- }
- }
-
- @Test
- public void testAdditionalConfigOnly() {
- final String [] allowed = {
- "bundle5", "bundle6"
- };
- final LoginAdminWhitelistConfiguration config = config(null, null, null, allowed);
- whitelist.activate(config);
-
- assertAdminLogin("bundle5", true);
- assertAdminLogin("bundle6", true);
- assertAdminLogin("foo.1.bar", false);
- assertAdminLogin(TYPICAL_DEFAULT_ALLOWED_BSN, true);
-
- for(String bsn : config.whitelist_bundles_default()) {
- assertAdminLogin(bsn, true);
- }
-
- for(String bsn : randomBsn()) {
- assertAdminLogin(bsn, false);
- }
- }
-
- @Test
- public void testDefaultAndAdditionalConfig() {
- whitelist.activate(config(null, null, new String [] { "defB"}, new String [] { "addB"}));
-
- assertAdminLogin("defB", true);
- assertAdminLogin("addB", true);
- assertAdminLogin("foo.1.bar", false);
- assertAdminLogin(TYPICAL_DEFAULT_ALLOWED_BSN, false);
-
- for(String bsn : randomBsn()) {
- assertAdminLogin(bsn, false);
- }
- }
-
- @Test
- public void testRegexpWhitelist() {
- final String [] allowed = {
- "bundle3", "bundle4"
- };
- whitelist.activate(config(null, "foo.*bar", allowed, null));
-
- assertAdminLogin("bundle3", true);
- assertAdminLogin("bundle4", true);
- assertAdminLogin("foo.2.bar", true);
- assertAdminLogin("foo.somethingElse.bar", true);
- assertAdminLogin(TYPICAL_DEFAULT_ALLOWED_BSN, false);
-
- for(String bsn : randomBsn()) {
- assertAdminLogin(bsn, false);
- }
- }
-
-
- private LoginAdminWhitelistConfiguration config(final Boolean bypass, final String regexp, final String[] defaultBSNs, final String[] additionalBSNs) {
- return new LoginAdminWhitelistConfiguration() {
- @Override
- public boolean whitelist_bypass() {
- return defaultIfNull(bypass, "whitelist_bypass");
- }
-
- @Override
- public String whitelist_bundles_regexp() {
- return defaultIfNull(regexp, "whitelist_bundles_regexp");
- }
-
- @Override
- public String[] whitelist_bundles_default() {
- return defaultIfNull(defaultBSNs, "whitelist_bundles_default");
- }
-
- @Override
- public String[] whitelist_bundles_additional() {
- return defaultIfNull(additionalBSNs, "whitelist_bundles_additional");
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return LoginAdminWhitelistConfiguration.class;
- }
-
- private <T> T defaultIfNull(final T value, final String methodName) {
- if (value != null) {
- return value;
- }
- try {
- return (T)this.annotationType().getMethod(methodName).getDefaultValue();
- } catch (NoSuchMethodException e) {
- return null;
- }
- }
- };
- }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/oak/server/it/OakServerTestSupport.java b/src/test/java/org/apache/sling/jcr/oak/server/it/OakServerTestSupport.java
index 3dc19b2..b816715 100644
--- a/src/test/java/org/apache/sling/jcr/oak/server/it/OakServerTestSupport.java
+++ b/src/test/java/org/apache/sling/jcr/oak/server/it/OakServerTestSupport.java
@@ -202,7 +202,7 @@
}
protected Option getWhitelistRegexpOption() {
- return newConfiguration("org.apache.sling.jcr.oak.server.internal.LoginAdminWhitelist")
+ return newConfiguration("org.apache.sling.jcr.base.internal.LoginAdminWhitelist")
.put("whitelist.bundles.regexp", "PAXEXAM-PROBE-.*")
.asOption();
}