SLING-5135 - Whitelist legit usages of loginAdministrative and administrative ResourceResolver

- move LoginAdminWhitelist from o.a.s.jcr.base to o.a.s.jcr.oak-server and do NOT export it

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1769105 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/jcr/base/LoginAdminWhitelist.java b/src/main/java/org/apache/sling/jcr/base/LoginAdminWhitelist.java
deleted file mode 100644
index 5f1763e..0000000
--- a/src/main/java/org/apache/sling/jcr/base/LoginAdminWhitelist.java
+++ /dev/null
@@ -1,33 +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.base;
-
-import org.apache.sling.jcr.api.SlingRepository;
-import org.osgi.annotation.versioning.ProviderType;
-import org.osgi.framework.Bundle;
-
-/**
- * Whitelist that defines which bundles can use the
- * {@link SlingRepository#loginAdministrative} method.
- */
-@ProviderType
-public interface LoginAdminWhitelist {
-
-    boolean allowLoginAdministrative(Bundle b);
-}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/DefaultWhitelist.java b/src/main/java/org/apache/sling/jcr/base/internal/DefaultWhitelist.java
deleted file mode 100644
index 072108d..0000000
--- a/src/main/java/org/apache/sling/jcr/base/internal/DefaultWhitelist.java
+++ /dev/null
@@ -1,47 +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.base.internal;
-
-/**
- * Define the default whitelist in its own class to better
- * keep track of it. The goal is to reduce it to the bare
- * minimum over time.
- */
-class DefaultWhitelist {
-    // TODO: remove bundles as their dependency on admin login is fixed, see SLING-5355 for linked issues
-    static final String [] WHITELISTED_BSN = {
-            "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"
-    };
-}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistImpl.java b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistImpl.java
deleted file mode 100644
index 3d7cafa..0000000
--- a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistImpl.java
+++ /dev/null
@@ -1,144 +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.base.internal;
-
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Service;
-import org.apache.sling.commons.osgi.PropertiesUtil;
-import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.jcr.base.LoginAdminWhitelist;
-import org.osgi.framework.Bundle;
-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.
- */
-@Service(value=LoginAdminWhitelist.class)
-@Component(
-        label="Apache Sling Login Admin Whitelist",
-        description="Defines which bundles can use SlingRepository.loginAdministrative()",
-        metatype=true)
-public class LoginAdminWhitelistImpl implements LoginAdminWhitelist {
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    /** 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.
-     */
-    @Property(
-            label="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.",
-            boolValue=false)
-    public static final String PROP_BYPASS_WHITELIST = "whitelist.bypass";
-    public static final boolean DEFAULT_BYPASS = false;
-    private boolean bypassWhitelist = DEFAULT_BYPASS;
-
-    @Property(
-            label="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.",
-            value = "")
-    public static final String PROP_WHITELIST_REGEXP = "whitelist.bundles.regexp";
-    private Pattern whitelistRegexp;
-
-    @Property(
-            label="Default whitelisted BSNs",
-            description="Default list of bundle symbolic names for which loginAdministrative() is allowed",
-            value = {})
-    public static final String PROP_DEFAULT_WHITELISTED_BSN = "whitelist.bundles.default";
-
-    @Property(
-            label="Additional whitelisted BSNs",
-            description="Additional list of bundle symbolic names for which loginAdministrative() is allowed",
-            value = {})
-    public static final String PROP_ADDITIONAL_WHITELISTED_BSN = "whitelist.bundles.additional";
-
-    private Set<String> whitelistedBsn;
-
-    public void activate(Map<String, Object> config) {
-        bypassWhitelist = PropertiesUtil.toBoolean(config.get(PROP_BYPASS_WHITELIST), DEFAULT_BYPASS);
-        whitelistedBsn = new TreeSet<String>();
-
-        final Object defBsns = config.get(PROP_DEFAULT_WHITELISTED_BSN);
-        if(defBsns == null) {
-            whitelistedBsn.addAll(Arrays.asList(DefaultWhitelist.WHITELISTED_BSN));
-        } else {
-            whitelistedBsn.addAll(Arrays.asList(PropertiesUtil.toStringArray(defBsns)));
-        }
-
-        final Object addBsns = config.get(PROP_ADDITIONAL_WHITELISTED_BSN);
-        if(addBsns != null) {
-            whitelistedBsn.addAll(Arrays.asList(PropertiesUtil.toStringArray(addBsns)));
-        }
-
-        final String regexp = PropertiesUtil.toString(config.get(PROP_WHITELIST_REGEXP), "");
-        if(regexp.trim().length() > 0) {
-            whitelistRegexp = Pattern.compile(regexp);
-            log.warn("A {} is configured, this is NOT RECOMMENDED for production: {}", PROP_WHITELIST_REGEXP, whitelistRegexp);
-        } else {
-            whitelistRegexp = null;
-        }
-
-        if(bypassWhitelist) {
-            log.info("bypassWhitelist={}, whitelisted BSNs=<ALL>", bypassWhitelist);
-            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={}, whitelisted BSNs({})={}",
-                    new Object[] { bypassWhitelist, whitelistedBsn.size(), whitelistedBsn });
-        }
-    }
-
-    @Override
-    public 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/test/java/org/apache/sling/jcr/base/MockLoginAdminWhitelist.java b/src/test/java/org/apache/sling/jcr/base/MockLoginAdminWhitelist.java
deleted file mode 100644
index deb84ff..0000000
--- a/src/test/java/org/apache/sling/jcr/base/MockLoginAdminWhitelist.java
+++ /dev/null
@@ -1,37 +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.base;
-
-import org.osgi.framework.Bundle;
-
-/** Mock LoginAdminWhitelist */
-public class MockLoginAdminWhitelist implements LoginAdminWhitelist {
-
-    private boolean allowAll;
-
-    public MockLoginAdminWhitelist(final boolean allowAll) {
-        this.allowAll = allowAll;
-    }
-
-    @Override
-    public boolean allowLoginAdministrative(Bundle b) {
-        return allowAll;
-    }
-    
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java b/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java
index 6539905..74b6f91 100644
--- a/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java
+++ b/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java
@@ -20,8 +20,11 @@
 
 import static org.junit.Assert.fail;
 
+import java.util.Arrays;
 import java.util.Dictionary;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Set;
 
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
@@ -33,20 +36,25 @@
 /** Minimal AbstractSlingRepositoryManager used for testing */
 public class MockSlingRepositoryManager extends AbstractSlingRepositoryManager {
 
-    private final Repository repository;
+    public static final String WHITELIST_ALL = "*";
 
-    private LoginAdminWhitelist loginAdminWhitelist;
+    public static final String WHITELIST_NONE = "";
+
+    private final Repository repository;
 
     private boolean loginAdminDisabled;
 
+    private Set<String> loginAdminWhitelist;
+
     public MockSlingRepositoryManager(Repository repository) {
-        this(repository, false, new MockLoginAdminWhitelist(true));
+        this(repository, false, WHITELIST_ALL);
     }
 
-    public MockSlingRepositoryManager(Repository repository, boolean loginAdminDisabled, LoginAdminWhitelist loginAdminWhitelist) {
+    public MockSlingRepositoryManager(Repository repository, boolean loginAdminDisabled, String... loginAdminWhitelist) {
         this.repository = repository;
         this.loginAdminDisabled = loginAdminDisabled;
-        this.loginAdminWhitelist = loginAdminWhitelist;
+        this.loginAdminWhitelist = new HashSet<>(Arrays.asList(loginAdminWhitelist));
+        this.loginAdminWhitelist.remove(WHITELIST_NONE);
     }
 
     @Override
@@ -86,7 +94,7 @@
 
     @Override
     protected boolean allowLoginAdministrativeForBundle(final Bundle bundle) {
-        return loginAdminWhitelist.allowLoginAdministrative(bundle);
+        return loginAdminWhitelist.contains("*") || loginAdminWhitelist.contains(bundle.getSymbolicName());
     }
 
     public void activate(BundleContext context) {
diff --git a/src/test/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistImplTest.java b/src/test/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistImplTest.java
deleted file mode 100644
index e621be4..0000000
--- a/src/test/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistImplTest.java
+++ /dev/null
@@ -1,160 +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.base.internal;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.osgi.framework.Bundle;
-
-public class LoginAdminWhitelistImplTest {
-    private LoginAdminWhitelistImpl whitelist;
-    private Map<String, Object> config;
-    private static final String TYPICAL_DEFAULT_ALLOWED_BSN = "org.apache.sling.jcr.base";
-    
-    @Before
-    public void setup() {
-        whitelist = new LoginAdminWhitelistImpl();
-        config = new HashMap<String, Object>();
-    }
-    
-    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() {
-        whitelist.activate(config);
-        
-        for(String bsn : DefaultWhitelist.WHITELISTED_BSN) {
-            assertAdminLogin(bsn, true);
-        }
-        
-        assertAdminLogin(TYPICAL_DEFAULT_ALLOWED_BSN, true);
-        
-        for(String bsn : randomBsn()) {
-            assertAdminLogin(bsn, false);
-        }
-    }
-    
-    @Test
-    public void testBypassWhitelist() {
-        config.put(LoginAdminWhitelistImpl.PROP_BYPASS_WHITELIST, true);
-        whitelist.activate(config);
-        
-        for(String bsn : randomBsn()) {
-            assertAdminLogin(bsn, true);
-        }
-    }
-    
-    @Test
-    public void testDefaultConfigOnly() {
-        final String [] allowed = {
-                "bundle1", "bundle2"
-        };
-        config.put(LoginAdminWhitelistImpl.PROP_DEFAULT_WHITELISTED_BSN, allowed);
-        whitelist.activate(config);
-        
-        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"
-        };
-        config.put(LoginAdminWhitelistImpl.PROP_ADDITIONAL_WHITELISTED_BSN, allowed);
-        whitelist.activate(config);
-        
-        assertAdminLogin("bundle5", true);
-        assertAdminLogin("bundle6", true);
-        assertAdminLogin("foo.1.bar", false);
-        
-        for(String bsn : DefaultWhitelist.WHITELISTED_BSN) {
-            assertAdminLogin(bsn, true);
-        }
-        
-        for(String bsn : randomBsn()) {
-            assertAdminLogin(bsn, false);
-        }
-    }
-    
-    @Test
-    public void testDefaultAndAdditionalConfig() {
-        config.put(LoginAdminWhitelistImpl.PROP_DEFAULT_WHITELISTED_BSN, new String [] { "defB"});
-        config.put(LoginAdminWhitelistImpl.PROP_ADDITIONAL_WHITELISTED_BSN, new String [] { "addB"});
-        whitelist.activate(config);
-        
-        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"
-        };
-        config.put(LoginAdminWhitelistImpl.PROP_DEFAULT_WHITELISTED_BSN, allowed);
-        config.put(LoginAdminWhitelistImpl.PROP_WHITELIST_REGEXP, "foo.*bar");
-        whitelist.activate(config);
-        
-        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);
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java b/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
index 9822191..dda31d8 100644
--- a/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
+++ b/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.sling.jcr.base.internal;
 
+import static org.apache.sling.jcr.base.MockSlingRepositoryManager.WHITELIST_ALL;
+import static org.apache.sling.jcr.base.MockSlingRepositoryManager.WHITELIST_NONE;
 import static org.junit.Assert.assertEquals;
 
 import java.util.ArrayList;
@@ -30,15 +32,10 @@
 
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.base.AbstractSlingRepository2;
-import org.apache.sling.jcr.base.LoginAdminWhitelist;
-import org.apache.sling.jcr.base.MockLoginAdminWhitelist;
 import org.apache.sling.jcr.base.MockSlingRepositoryManager;
 import org.apache.sling.testing.mock.jcr.MockJcr;
 import org.apache.sling.testing.mock.osgi.MockOsgi;
-import org.apache.sling.testing.mock.sling.ResourceResolverType;
-import org.apache.sling.testing.mock.sling.junit.SlingContext;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -80,7 +77,7 @@
         BundleContext bundleContext = MockOsgi.newBundleContext();
         Bundle bundle = bundleContext.getBundle();
 
-        LoginAdminWhitelist whitelist = new MockLoginAdminWhitelist(whitelistAllowsLoginAdmin);
+        String whitelist = whitelistAllowsLoginAdmin ? WHITELIST_ALL : WHITELIST_NONE;
 
         final MockSlingRepositoryManager repoMgr =
                 new MockSlingRepositoryManager(MockJcr.newRepository(), !managerAllowsLoginAdmin, whitelist);