Merge pull request #11 from apache/issues/SLING-11741

Issues/sling 11741
diff --git a/pom.xml b/pom.xml
index d4708af..237db2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling-bundle-parent</artifactId>
-        <version>49</version>
+        <version>52</version>
         <relativePath />
     </parent>
 
@@ -83,15 +83,15 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.jcr.api</artifactId>
-            <version>2.4.0</version>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.util.converter</artifactId>
+            <version>1.0.9</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.commons.osgi</artifactId>
-            <version>2.4.2</version>
+            <artifactId>org.apache.sling.jcr.api</artifactId>
+            <version>2.4.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -146,26 +146,32 @@
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.testing.osgi-mock</artifactId>
-            <version>3.3.6</version>
+            <artifactId>org.apache.sling.testing.osgi-mock.junit4</artifactId>
+            <version>3.3.8</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.testing.sling-mock</artifactId>
-            <version>3.4.4</version>
+            <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId>
+            <version>3.4.10</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.jcr-mock</artifactId>
-            <version>1.6.6</version>
+            <version>1.6.10</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.4.2</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.9.5</version>
+            <artifactId>mockito-core</artifactId>
+            <version>5.4.0</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
index 9082158..c91f7e0 100644
--- a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
@@ -441,12 +441,12 @@
      */
     @Override
     public final Session loginAdministrative(final String workspace) throws RepositoryException {
-        final boolean whitelisted = getSlingRepositoryManager().allowLoginAdministrativeForBundle(usingBundle);
+        final boolean allowed = getSlingRepositoryManager().allowLoginAdministrativeForBundle(usingBundle);
 
-        if(!whitelisted) {
+        if(!allowed) {
             final String symbolicName = usingBundle.getSymbolicName();
-            logger.error("Bundle {} is NOT whitelisted to use SlingRepository.loginAdministrative", symbolicName);
-            throw new LoginException("Bundle " + symbolicName +" is NOT whitelisted");
+            logger.error("Bundle {} is NOT allow listed to use SlingRepository.loginAdministrative", symbolicName);
+            throw new LoginException("Bundle " + symbolicName +" is NOT allow listed");
         } else if (this.getSlingRepositoryManager().isDisableLoginAdministrative()) {
             logger.error("SlingRepository.loginAdministrative is disabled. Please use SlingRepository.loginService.");
             throw new LoginException("SlingRepository.loginAdministrative is disabled.");
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
index e19ba44..1ea27a8 100644
--- a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
@@ -33,7 +33,7 @@
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.api.SlingRepositoryInitializer;
 import org.apache.sling.jcr.base.internal.loader.Loader;
-import org.apache.sling.jcr.base.internal.LoginAdminWhitelist;
+import org.apache.sling.jcr.base.internal.LoginAdminAllowList;
 import org.apache.sling.jcr.base.internal.mount.ProxyJackrabbitRepository;
 import org.apache.sling.jcr.base.internal.mount.ProxyRepository;
 import org.apache.sling.jcr.base.spi.RepositoryMount;
@@ -120,7 +120,7 @@
 
     private volatile Loader loader;
 
-    private volatile ServiceTracker<LoginAdminWhitelist, LoginAdminWhitelist> whitelistTracker;
+    private volatile ServiceTracker<LoginAdminAllowList, LoginAdminAllowList> allowListTracker;
 
     private final Object repoInitLock = new Object();
 
@@ -181,7 +181,7 @@
      *         to use {@code loginAdministrative}.
      */
     protected boolean allowLoginAdministrativeForBundle(final Bundle bundle) {
-        return whitelistTracker.getService().allowLoginAdministrative(bundle);
+        return allowListTracker.getService().allowLoginAdministrative(bundle);
     }
 
     /**
@@ -514,36 +514,36 @@
         this.repoInitializerTracker.open();
 
         // If allowLoginAdministrativeForBundle is overridden we assume we don't need
-        // a LoginAdminWhitelist service - that's the case if the derived class
-        // implements its own strategy and the LoginAdminWhitelist interface is
+        // a LoginAdminAllowList service - that's the case if the derived class
+        // implements its own strategy and the LoginAdminAllowList interface is
         // not exported by this bundle anyway, so cannot be implemented differently.
-        boolean enableWhitelist = !isAllowLoginAdministrativeForBundleOverridden();
-        final CountDownLatch waitForWhitelist = new CountDownLatch(enableWhitelist ? 1 : 0);
-        if (enableWhitelist) {
-            whitelistTracker = new ServiceTracker<LoginAdminWhitelist, LoginAdminWhitelist>(bundleContext, LoginAdminWhitelist.class, null) {
+        boolean enableAllowlist = !isAllowLoginAdministrativeForBundleOverridden();
+        final CountDownLatch waitForAllowList = new CountDownLatch(enableAllowlist ? 1 : 0);
+        if (enableAllowlist) {
+            allowListTracker = new ServiceTracker<LoginAdminAllowList, LoginAdminAllowList>(bundleContext, LoginAdminAllowList.class, null) {
                 @Override
-                public LoginAdminWhitelist addingService(final ServiceReference<LoginAdminWhitelist> reference) {
+                public LoginAdminAllowList addingService(final ServiceReference<LoginAdminAllowList> reference) {
                     try {
                         return super.addingService(reference);
                     } finally {
-                        waitForWhitelist.countDown();
+                        waitForAllowList.countDown();
                     }
                 }
             };
-            whitelistTracker.open();
+            allowListTracker.open();
         }
 
-        // start repository asynchronously to allow LoginAdminWhitelist to become available
-        // NOTE: making this conditional allows tests to register a mock whitelist before
+        // start repository asynchronously to allow LoginAdminAllowList to become available
+        // NOTE: making this conditional allows tests to register a mock allow list before
         // activating the RepositoryManager, so they don't need to deal with async startup
         startupThread = new Thread("Apache Sling Repository Startup Thread #" + startupCounter.incrementAndGet()) {
             @Override
             public void run() {
                 try {
-                    waitForWhitelist.await();
+                    waitForAllowList.await();
                     initializeAndRegisterRepositoryService();
                 } catch (InterruptedException e) {
-                    log.warn("Interrupted while waiting for the {} service, cancelling repository initialisation. {}", LoginAdminWhitelist.class.getSimpleName(), INTERRUPTED_EXCEPTION_NOTE, e);
+                    log.warn("Interrupted while waiting for the {} service, cancelling repository initialisation. {}", LoginAdminAllowList.class.getSimpleName(), INTERRUPTED_EXCEPTION_NOTE, e);
                     Thread.currentThread().interrupt();
                 }
             }
@@ -686,9 +686,9 @@
             repoInitializerTracker = null;
         }
 
-        if (whitelistTracker != null) {
-            whitelistTracker.close();
-            whitelistTracker = null;
+        if (allowListTracker != null) {
+            allowListTracker.close();
+            allowListTracker = null;
         }
 
         this.repositoryService = null;
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/WhitelistFragment.java b/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java
similarity index 65%
rename from src/main/java/org/apache/sling/jcr/base/internal/WhitelistFragment.java
rename to src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java
index 81184a4..2c00dde 100644
--- a/src/main/java/org/apache/sling/jcr/base/internal/WhitelistFragment.java
+++ b/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java
@@ -32,9 +32,9 @@
 import static java.util.Arrays.asList;
 
 @ObjectClassDefinition(
-        name = "Apache Sling Login Admin Whitelist Configuration Fragment",
-        description = "Whitelist configuration fragments contribute a list of whitelisted bundle symbolic " +
-                "names to the Login Admin Whitelist. This allows for modularisation of the whitelist."
+        name = "Apache Sling Login Admin Allow List Configuration Fragment",
+        description = "This list of Bundle Symbolic Names is added to the list of bundles which are allowed " +
+            "to use Administrative Login. The full list is built in a modular way out of all such configuration fragments."
 )
 @interface Configuration {
 
@@ -42,55 +42,55 @@
             name = "Name",
             description = "Optional name to disambiguate configurations."
     )
-    String whitelist_name() default "[unnamed]";
+    String allowlist_name() default "[unnamed]";
 
     @AttributeDefinition(
-            name = "Whitelisted BSNs",
+            name = "Allow listed BSNs",
             description = "A list of bundle symbolic names allowed to use loginAdministrative()."
     )
-    String[] whitelist_bundles();
+    String[] allowlist_bundles();
 
-    @SuppressWarnings("unused")
-    String webconsole_configurationFactory_nameHint() default "{whitelist.name}: [{whitelist.bundles}]";
+    @SuppressWarnings({"unused", "java:S100"})
+    String webconsole_configurationFactory_nameHint() default "{allowlist.name}: [{allowlist.bundles}]";
 }
 
 @Component(
-        configurationPid = "org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment",
+        configurationPid = AllowListFragment.FACTORY_PID,
         configurationPolicy = ConfigurationPolicy.REQUIRE,
-        service = WhitelistFragment.class
+        service = AllowListFragment.class
 )
 @Designate(ocd = Configuration.class, factory = true)
-public class WhitelistFragment {
+public class AllowListFragment {
 
-    private String name;
+    public static final String FACTORY_PID = "org.apache.sling.jcr.base.LoginAdminAllowList.fragment";
 
-    private Set<String> bundles;
+    final String name;
 
-    @SuppressWarnings("unused")
-    public WhitelistFragment() {
-        // default constructor for SCR
+    final Set<String> bundles;
+
+    /**
+     * Constructor for SCR
+     * @param config Configuration
+     */
+    @Activate
+    public AllowListFragment(final Configuration config) {
+        this.name = config.allowlist_name();
+        this.bundles = asSet(config.allowlist_bundles());
     }
 
     // constructor for tests and for backwards compatible deprecated fragments
-    WhitelistFragment(String name, String[] bundles) {
+    AllowListFragment(String name, String[] bundles) {
         this.name = name;
         this.bundles = asSet(bundles);
     }
 
-    @Activate
-    @SuppressWarnings("unused")
-    void activate(Configuration config) {
-        name = config.whitelist_name();
-        bundles = asSet(config.whitelist_bundles());
-    }
-
-    boolean allows(String bsn) {
+    boolean allows(final String bsn) {
         return bundles.contains(bsn);
     }
 
     @Override
     public String toString() {
-        return name + ": " + bundles + "";
+        return name + ": " + bundles;
     }
 
     @Override
@@ -98,10 +98,10 @@
         if (this == o) {
             return true;
         }
-        if (!(o instanceof WhitelistFragment)) {
+        if (!(o instanceof AllowListFragment)) {
             return false;
         }
-        final WhitelistFragment that = (WhitelistFragment) o;
+        final AllowListFragment that = (AllowListFragment) o;
         return name.equals(that.name)
                 && bundles.equals(that.bundles);
     }
@@ -114,6 +114,6 @@
     }
 
     private Set<String> asSet(final String[] values) {
-        return Collections.unmodifiableSet(new HashSet<String>(asList(values)));
+        return Collections.unmodifiableSet(new HashSet<>(asList(values)));
     }
 }
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/LegacyFragment.java b/src/main/java/org/apache/sling/jcr/base/internal/LegacyFragment.java
new file mode 100644
index 0000000..eae6249
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/LegacyFragment.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+
+/**
+ * Legacy fragment configuration. Use {@link AllowListFragment} instead.
+ */
+@Component(
+        configurationPid = LegacyFragment.LEGACY_FACTORY_PID,
+        configurationPolicy = ConfigurationPolicy.REQUIRE,
+        service = AllowListFragment.class
+)
+public class LegacyFragment extends AllowListFragment {
+
+    public static final String LEGACY_FACTORY_PID = "org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment";
+
+    public @interface Configuration {
+        @SuppressWarnings("java:S100")
+        String whitelist_name() default "[unnamed]";
+
+        @SuppressWarnings("java:S100")
+        String[] whitelist_bundles();
+    }
+
+
+    @Activate
+    public LegacyFragment(Configuration configuration) {
+        super(configuration.whitelist_name(), configuration.whitelist_bundles());
+        LoginAdminAllowList.LOG.warn("Using deprecated factory configuration '{}' with whitelist.name='{}'. " +
+            "Update your configuration to use configuration '{}' instead.", 
+            LEGACY_FACTORY_PID, configuration.whitelist_name(), AllowListFragment.FACTORY_PID);
+    }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java
new file mode 100644
index 0000000..ef85849
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java
@@ -0,0 +1,194 @@
+/*
+ * 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.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Pattern;
+
+import org.apache.sling.jcr.api.SlingRepository;
+import org.osgi.framework.Bundle;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.util.converter.Converters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Allow list 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 = LoginAdminAllowList.class, 
+    configurationPid = {LoginAdminAllowList.PID, LoginAdminAllowList.LEGACY_PID}
+)
+public class LoginAdminAllowList {
+
+    public static final String PID = "org.apache.sling.jcr.base.LoginAdminAllowList";
+
+    public static final String LEGACY_PID = "org.apache.sling.jcr.base.internal.LoginAdminWhitelist";
+
+    static final Logger LOG = LoggerFactory.getLogger(LoginAdminAllowList.class);
+
+    // for backwards compatibility only (read properties directly to prevent them from appearing in the metatype)
+    private static final String LEGACY_BYPASS_PROPERTY = "whitelist.bypass";
+
+    private static final String LEGACY_BUNDLES_PROPERTY = "whitelist.bundles.regexp";
+
+    private static final String PROP_LEGACY_BUNDLES_DEFAULT = "whitelist.bundles.default";
+
+    private static final String PROP_LEGACY_BUNDLES_ADDITIONAL = "whitelist.bundles.additional";
+
+    @SuppressWarnings("java:S3077")
+    // java:S3077 - the field is updated and read atomically, and the object is
+    // immutable, hence the use of "volatile" is adequate
+    private volatile ConfigurationState config;
+
+    private final List<AllowListFragment> allowListFragments = new CopyOnWriteArrayList<>();
+
+    private final Map<String, AllowListFragment> backwardsCompatibleFragments = new ConcurrentHashMap<>();
+
+    @Reference(
+            cardinality = ReferenceCardinality.MULTIPLE,
+            policy = ReferencePolicy.DYNAMIC,
+            policyOption = ReferencePolicyOption.GREEDY
+    )
+    void bindAllowListFragment(AllowListFragment fragment) {
+        allowListFragments.add(fragment);
+        LOG.info("AllowListFragment added '{}'", fragment);
+    }
+
+    void unbindAllowListFragment(AllowListFragment fragment) {
+        allowListFragments.remove(fragment);
+        LOG.info("AllowListFragment removed '{}'", fragment);
+    }
+
+    @Activate @Modified
+    void configure(final LoginAdminAllowListConfiguration configuration, final Map<String, Object> properties) {
+        this.config = new ConfigurationState(configuration, properties);
+        ensureBackwardsCompatibility(properties, PROP_LEGACY_BUNDLES_DEFAULT);
+        ensureBackwardsCompatibility(properties, PROP_LEGACY_BUNDLES_ADDITIONAL);
+    }
+
+    public boolean allowLoginAdministrative(Bundle b) {
+        // create local copy of ConfigurationState to avoid reading mixed configurations during an configure
+        final ConfigurationState localConfig = this.config;
+        if (localConfig == null) {
+            throw new IllegalStateException("LoginAdminAllowList has no configuration.");
+        }
+
+        if(localConfig.bypassAllowList) {
+            LOG.debug("Allow list is bypassed, all bundles allowed to use loginAdministrative");
+            return true;
+        }
+
+        final String bsn = b.getSymbolicName();
+
+        if(localConfig.allowListRegexp != null && localConfig.allowListRegexp.matcher(bsn).matches()) {
+            LOG.debug("{} is allow listed to use loginAdministrative, by regexp", bsn);
+            return true;
+        }
+
+        for (final AllowListFragment fragment : allowListFragments) {
+            if (fragment.allows(bsn)) {
+                LOG.debug("{} is allow listed to use loginAdministrative, by allow list fragment '{}'",
+                        bsn, fragment);
+                return true;
+            }
+        }
+
+        LOG.debug("{} is not allow listed to use loginAdministrative", bsn);
+        return false;
+    }
+
+    // encapsulate configuration state for atomic configuration updates
+    static class ConfigurationState {
+
+        public final boolean bypassAllowList;
+
+        public final Pattern allowListRegexp;
+
+        ConfigurationState(final LoginAdminAllowListConfiguration config, final Map<String, Object> properties) {
+            // first check for legacy properties
+            boolean bypass = config.allowlist_bypass();
+            final Object legacyBypassObject = properties.get(LEGACY_BYPASS_PROPERTY);
+            if (legacyBypassObject != null) {
+                LOG.warn("Using deprecated configuration property '{}' from configuration '{}'. " +
+                    "Update your configuration to use configuration '{}' and property '{}' instead.", 
+                    LEGACY_BYPASS_PROPERTY, LEGACY_PID, PID, "allowlist.bypass");
+                bypass = Converters.standardConverter().convert(legacyBypassObject).defaultValue(false).to(Boolean.class);
+            }
+            String legacyRegexp = null;
+            final Object legacyBundlesObject = properties.get(LEGACY_BUNDLES_PROPERTY);
+            if (legacyBypassObject != null) {
+                LOG.warn("Using deprecated configuration property '{}' from configuration '{}'. " +
+                    "Update your configuration to use configuration '{}' and property '{}' instead.", 
+                    LEGACY_BUNDLES_PROPERTY, LEGACY_PID, PID, "allowlist.bundles.regexp");
+                legacyRegexp = Converters.standardConverter().convert(legacyBundlesObject).to(String.class);
+            }
+
+            final String regexp = config.allowlist_bundles_regexp();
+            if (regexp.trim().length() > 0) {
+                if (legacyRegexp != null) {
+                    LOG.warn("Both deprecated configuration property '{}' and non-deprecated configuration property '{}' are set. " +
+                        "The deprecated property '{}' is ignored.", 
+                        LEGACY_BUNDLES_PROPERTY, "allowlist.bundles.regexp", LEGACY_BUNDLES_PROPERTY);
+                }
+                this.allowListRegexp = Pattern.compile(regexp);
+            } else {
+                this.allowListRegexp = legacyRegexp != null ? Pattern.compile(legacyRegexp) : null;
+            }
+            if (this.allowListRegexp != null) {
+                LOG.warn("A 'allowlist.bundles.regexp' is configured, this is NOT RECOMMENDED for production: {}", allowListRegexp);
+            }
+            this.bypassAllowList = bypass;
+            if (this.bypassAllowList) {
+                LOG.info("allowlist.bypass=true, allowed BSNs=<ALL>");
+                LOG.warn("All bundles are allowed to use loginAdministrative due to the 'allowlist.bypass' " +
+                        "configuration of this service. This is NOT RECOMMENDED, for security reasons."
+                );
+            }
+        }
+    }
+
+    private void ensureBackwardsCompatibility(final Map<String, Object> properties, final String propertyName) {
+        final AllowListFragment oldFragment = backwardsCompatibleFragments.remove(propertyName);
+        
+        final String[] bsns = Converters.standardConverter().convert(properties.get(propertyName)).to(String[].class);        
+        if (bsns != null && bsns.length != 0) {
+            LOG.warn("Using deprecated configuration property '{}'", propertyName);
+            final AllowListFragment fragment = new AllowListFragment("deprecated-" + propertyName, bsns);
+            bindAllowListFragment(fragment);
+            backwardsCompatibleFragments.put(propertyName, fragment);
+        }
+        
+        if (oldFragment != null) {
+            unbindAllowListFragment(oldFragment);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistConfiguration.java b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowListConfiguration.java
similarity index 86%
rename from src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistConfiguration.java
rename to src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowListConfiguration.java
index 62df245..a3e625e 100644
--- a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistConfiguration.java
+++ b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowListConfiguration.java
@@ -22,24 +22,25 @@
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 
 @ObjectClassDefinition(
-    name = "Apache Sling Login Admin Whitelist",
+    pid = LoginAdminAllowList.PID,
+    name = "Apache Sling Login Admin Allowlist",
     description = "Defines which bundles can use SlingRepository.loginAdministrative()"
 )
-@interface LoginAdminWhitelistConfiguration {
+@interface LoginAdminAllowListConfiguration {
 
     /**
-     * Need to allow for bypassing the whitelist, for backwards
+     * Need to allow for bypassing the allowlist, 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",
+        name = "Bypass the allowlist",
         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;
+    boolean allowlist_bypass() default false;
 
     /**
      * Regular expression for bundle symbolic names for which loginAdministrative()
@@ -54,10 +55,10 @@
      * @return The configured regular exression.
      */
     @AttributeDefinition(
-            name = "Whitelist regexp",
+            name = "Allowlist 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 "";
+    String allowlist_bundles_regexp() default "";
 }
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelist.java b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelist.java
deleted file mode 100644
index 5bd5d38..0000000
--- a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelist.java
+++ /dev/null
@@ -1,172 +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.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-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.component.annotations.Modified;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
-import org.osgi.service.component.annotations.ReferencePolicy;
-import org.osgi.service.component.annotations.ReferencePolicyOption;
-import org.osgi.service.metatype.annotations.Designate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.sling.commons.osgi.PropertiesUtil.toStringArray;
-
-/**
- * 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 static final Logger LOG = LoggerFactory.getLogger(LoginAdminWhitelist.class);
-
-    private volatile ConfigurationState config;
-
-    private final List<WhitelistFragment> whitelistFragments = new CopyOnWriteArrayList<WhitelistFragment>();
-
-    // for backwards compatibility only (read properties directly to prevent them from appearing in the metatype)
-    private static final String PROP_WHITELIST_BUNDLES_DEFAULT = "whitelist.bundles.default";
-
-    private static final String PROP_WHITELIST_BUNDLES_ADDITIONAL = "whitelist.bundles.additional";
-
-    private final Map<String, WhitelistFragment> backwardsCompatibleFragments =
-            new ConcurrentHashMap<String, WhitelistFragment>();
-
-    @Reference(
-            cardinality = ReferenceCardinality.MULTIPLE,
-            policy = ReferencePolicy.DYNAMIC,
-            policyOption = ReferencePolicyOption.GREEDY
-    ) @SuppressWarnings("unused")
-    void bindWhitelistFragment(WhitelistFragment fragment) {
-        whitelistFragments.add(fragment);
-        LOG.info("WhitelistFragment added '{}'", fragment);
-    }
-
-    @SuppressWarnings("unused")
-    void unbindWhitelistFragment(WhitelistFragment fragment) {
-        whitelistFragments.remove(fragment);
-        LOG.info("WhitelistFragment removed '{}'", fragment);
-    }
-
-    @Activate @Modified @SuppressWarnings("unused")
-    void configure(LoginAdminWhitelistConfiguration configuration, Map<String, Object> properties) {
-        this.config = new ConfigurationState(configuration);
-        ensureBackwardsCompatibility(properties, PROP_WHITELIST_BUNDLES_DEFAULT);
-        ensureBackwardsCompatibility(properties, PROP_WHITELIST_BUNDLES_ADDITIONAL);
-    }
-
-    public boolean allowLoginAdministrative(Bundle b) {
-        if (config == null) {
-            throw new IllegalStateException("LoginAdminWhitelist has no configuration.");
-        }
-        // create local copy of ConfigurationState to avoid reading mixed configurations during an configure
-        final ConfigurationState localConfig = this.config;
-        if(localConfig.bypassWhitelist) {
-            LOG.debug("Whitelist is bypassed, all bundles allowed to use loginAdministrative");
-            return true;
-        }
-
-        final String bsn = b.getSymbolicName();
-
-        if(localConfig.whitelistRegexp != null && localConfig.whitelistRegexp.matcher(bsn).matches()) {
-            LOG.debug("{} is whitelisted to use loginAdministrative, by regexp", bsn);
-            return true;
-        }
-
-        for (final WhitelistFragment fragment : whitelistFragments) {
-            if (fragment.allows(bsn)) {
-                LOG.debug("{} is whitelisted to use loginAdministrative, by whitelist fragment '{}'",
-                        bsn, fragment);
-                return true;
-            }
-        }
-
-        LOG.debug("{} is not whitelisted to use loginAdministrative", bsn);
-        return false;
-    }
-
-    // encapsulate configuration state for atomic configuration updates
-    private static class ConfigurationState {
-
-        private final boolean bypassWhitelist;
-
-        private final Pattern whitelistRegexp;
-
-        private ConfigurationState(final LoginAdminWhitelistConfiguration config) {
-            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 'whitelist.bypass' " +
-                        "configuration of this service. This is NOT RECOMMENDED, for security reasons."
-                );
-            }
-        }
-    }
-
-    @SuppressWarnings("deprecated")
-    private void ensureBackwardsCompatibility(final Map<String, Object> properties, final String propertyName) {
-        final WhitelistFragment oldFragment = backwardsCompatibleFragments.remove(propertyName);
-        
-        final String[] bsns = toStringArray(properties.get(propertyName), new String[0]);
-        if (bsns.length != 0) {
-            LOG.warn("Using deprecated configuration property '{}'", propertyName);
-            final WhitelistFragment fragment = new WhitelistFragment("deprecated-" + propertyName, bsns);
-            bindWhitelistFragment(fragment);
-            backwardsCompatibleFragments.put(propertyName, fragment);
-        }
-        
-        if (oldFragment != null) {
-            unbindWhitelistFragment(oldFragment);
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitSession.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitSession.java
index 97ee586..ba529e7 100644
--- a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitSession.java
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitSession.java
@@ -18,7 +18,6 @@
  */
 package org.apache.sling.jcr.base.internal.mount;
 
-import java.util.HashSet;
 import java.util.Set;
 import javax.jcr.AccessDeniedException;
 import javax.jcr.Item;
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 cecb298..c3de220 100644
--- a/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java
+++ b/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java
@@ -36,25 +36,25 @@
 /** Minimal AbstractSlingRepositoryManager used for testing */
 public class MockSlingRepositoryManager extends AbstractSlingRepositoryManager {
 
-    public static final String WHITELIST_ALL = "*";
+    public static final String ALLOWLIST_ALL = "*";
 
-    public static final String WHITELIST_NONE = "";
+    public static final String ALLOWLIST_NONE = "";
 
     private final Repository repository;
 
     private boolean loginAdminDisabled;
 
-    private Set<String> loginAdminWhitelist;
+    private Set<String> loginAdminAllowList;
 
     public MockSlingRepositoryManager(Repository repository) {
-        this(repository, false, WHITELIST_ALL);
+        this(repository, false, ALLOWLIST_ALL);
     }
 
-    public MockSlingRepositoryManager(Repository repository, boolean loginAdminDisabled, String... loginAdminWhitelist) {
+    public MockSlingRepositoryManager(Repository repository, boolean loginAdminDisabled, String... loginAdminAllowList) {
         this.repository = repository;
         this.loginAdminDisabled = loginAdminDisabled;
-        this.loginAdminWhitelist = new HashSet<>(Arrays.asList(loginAdminWhitelist));
-        this.loginAdminWhitelist.remove(WHITELIST_NONE);
+        this.loginAdminAllowList = new HashSet<>(Arrays.asList(loginAdminAllowList));
+        this.loginAdminAllowList.remove(ALLOWLIST_NONE);
     }
 
     @Override
@@ -94,7 +94,7 @@
 
     @Override
     protected boolean allowLoginAdministrativeForBundle(final Bundle bundle) {
-        return loginAdminWhitelist.contains("*") || loginAdminWhitelist.contains(bundle.getSymbolicName());
+        return loginAdminAllowList.contains("*") || loginAdminAllowList.contains(bundle.getSymbolicName());
     }
 
     public void activate(BundleContext context) {
diff --git a/src/test/java/org/apache/sling/jcr/base/RepositoryInitializersTest.java b/src/test/java/org/apache/sling/jcr/base/RepositoryInitializersTest.java
index 16832f0..d6f0b8b 100644
--- a/src/test/java/org/apache/sling/jcr/base/RepositoryInitializersTest.java
+++ b/src/test/java/org/apache/sling/jcr/base/RepositoryInitializersTest.java
@@ -83,7 +83,7 @@
     private void registerInitializer(String id, int serviceRanking) {
         final SlingRepositoryInitializer init = new TestInitializer(id);
         final Hashtable<String, Object> props = new Hashtable<String, Object>();
-        props.put(Constants.SERVICE_RANKING, new Integer(serviceRanking));
+        props.put(Constants.SERVICE_RANKING, serviceRanking);
         context.bundleContext().registerService(SlingRepositoryInitializer.class.getName(), init, props);
     }
     
diff --git a/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java b/src/test/java/org/apache/sling/jcr/base/internal/AllowListWiringTest.java
similarity index 87%
rename from src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
rename to src/test/java/org/apache/sling/jcr/base/internal/AllowListWiringTest.java
index ef14d62..e02b476 100644
--- a/src/test/java/org/apache/sling/jcr/base/internal/WhitelistWiringTest.java
+++ b/src/test/java/org/apache/sling/jcr/base/internal/AllowListWiringTest.java
@@ -18,8 +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.apache.sling.jcr.base.MockSlingRepositoryManager.ALLOWLIST_ALL;
+import static org.apache.sling.jcr.base.MockSlingRepositoryManager.ALLOWLIST_NONE;
 import static org.junit.Assert.assertEquals;
 
 import java.util.ArrayList;
@@ -44,19 +44,19 @@
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
-/** Verify that the AbstractSlingRepository2 uses the login admin whitelist,
+/** Verify that the AbstractSlingRepository2 uses the login admin allow list,
  *  as well as its combination with the global "disable login admin" flag
  */
 @RunWith(Parameterized.class)
-public class WhitelistWiringTest {
+public class AllowListWiringTest {
 
     private SlingRepository repository;
 
     private final boolean managerAllowsLoginAdmin;
-    private final boolean whitelistAllowsLoginAdmin;
+    private final boolean allowListAllowsLoginAdmin;
     private final boolean loginAdminExpected;
  
-    @Parameters(name="manager {0}, whitelist {1} -> {2}")
+    @Parameters(name="manager {0}, allow list {1} -> {2}")
     public static Collection<Object[]> data() {
         final List<Object[]> result = new ArrayList<Object[]>();
         result.add(new Object[] { false, false, false });
@@ -66,9 +66,9 @@
         return result;
     }
 
-    public WhitelistWiringTest(boolean managerAllowsLoginAdmin, boolean whitelistAllowsLoginAdmin, boolean loginAdminExpected) {
+    public AllowListWiringTest(boolean managerAllowsLoginAdmin, boolean allowListAllowsLoginAdmin, boolean loginAdminExpected) {
         this.managerAllowsLoginAdmin = managerAllowsLoginAdmin;
-        this.whitelistAllowsLoginAdmin = whitelistAllowsLoginAdmin;
+        this.allowListAllowsLoginAdmin = allowListAllowsLoginAdmin;
         this.loginAdminExpected = loginAdminExpected;
     }
     
@@ -77,10 +77,10 @@
         BundleContext bundleContext = MockOsgi.newBundleContext();
         Bundle bundle = bundleContext.getBundle();
 
-        String whitelist = whitelistAllowsLoginAdmin ? WHITELIST_ALL : WHITELIST_NONE;
+        String allowList = allowListAllowsLoginAdmin ? ALLOWLIST_ALL : ALLOWLIST_NONE;
 
         final MockSlingRepositoryManager repoMgr =
-                new MockSlingRepositoryManager(MockJcr.newRepository(), !managerAllowsLoginAdmin, whitelist);
+                new MockSlingRepositoryManager(MockJcr.newRepository(), !managerAllowsLoginAdmin, allowList);
 
         repoMgr.activate(bundleContext);
         
diff --git a/src/test/java/org/apache/sling/jcr/base/internal/LegacyFragmentTest.java b/src/test/java/org/apache/sling/jcr/base/internal/LegacyFragmentTest.java
new file mode 100644
index 0000000..62556cb
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/base/internal/LegacyFragmentTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.osgi.util.converter.Converters;
+
+public class LegacyFragmentTest {
+
+    private final LegacyFragment.Configuration configuration;
+    {
+        final Map<String, Object> props = new HashMap<>();
+        props.put("whitelist.name", "test");
+        props.put("whitelist.bundles", new String[] { "org.apache.sling.test" });
+        configuration = Converters.standardConverter()
+                .convert(props)
+                .to(LegacyFragment.Configuration.class);
+    }
+
+    @Test
+    public void testFragmentBinding() {
+        final AllowListFragment fragment = new LegacyFragment(configuration);
+        assertEquals("test", fragment.name);
+        assertTrue(fragment.allows("org.apache.sling.test"));
+        assertFalse(fragment.allows("org.apache.sling.test.not.allowed"));
+        assertEquals(1, fragment.bundles.size());
+    }
+
+    @Test
+    public void testEquality() {
+        final LegacyFragment legacyFragment = new LegacyFragment(configuration);
+        final AllowListFragment fragment = new AllowListFragment(configuration.whitelist_name(), configuration.whitelist_bundles());
+
+        assertEquals(fragment, legacyFragment);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistTest.java b/src/test/java/org/apache/sling/jcr/base/internal/LoginAdminAllowListTest.java
similarity index 62%
rename from src/test/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistTest.java
rename to src/test/java/org/apache/sling/jcr/base/internal/LoginAdminAllowListTest.java
index bf08545..7c2724f 100644
--- a/src/test/java/org/apache/sling/jcr/base/internal/LoginAdminWhitelistTest.java
+++ b/src/test/java/org/apache/sling/jcr/base/internal/LoginAdminAllowListTest.java
@@ -19,6 +19,8 @@
 package org.apache.sling.jcr.base.internal;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
@@ -33,19 +35,19 @@
 import org.osgi.framework.Bundle;
 import org.osgi.service.cm.ConfigurationException;
 
-public class LoginAdminWhitelistTest {
+public class LoginAdminAllowListTest {
 
-    private LoginAdminWhitelist whitelist;
+    private LoginAdminAllowList allowList;
 
     @Before
     public void setup() {
-        whitelist = new LoginAdminWhitelist();
+        allowList = new LoginAdminAllowList();
     }
     
     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);
+        final boolean actual = allowList.allowLoginAdministrative(b);
         assertEquals("For bundle " + bundleSymbolicName + ", expected admin login=" + expected, expected, actual);
     }
     
@@ -58,8 +60,8 @@
     }
 
     @Test
-    public void testBypassWhitelist() throws ConfigurationException {
-        configure(whitelist, true, null, null, null);
+    public void testBypassAllowList() throws ConfigurationException {
+        configure(allowList, true, null, null, null);
         
         for(String bsn : randomBsn()) {
             assertAdminLogin(bsn, true);
@@ -71,7 +73,7 @@
         final String [] allowed = {
                 "bundle1", "bundle2"
         };
-        configure(whitelist, null, null, allowed, null);
+        configure(allowList, null, null, allowed, null);
 
         assertAdminLogin("foo.1.bar", false);
 
@@ -89,7 +91,7 @@
         final String [] allowed = {
                 "bundle5", "bundle6"
         };
-        configure(whitelist, null, null, null, allowed);
+        configure(allowList, null, null, null, allowed);
 
         assertAdminLogin("foo.1.bar", false);
 
@@ -104,7 +106,7 @@
     
     @Test
     public void testDefaultAndAdditionalConfig() throws ConfigurationException {
-        configure(whitelist, null, null, new String [] { "defB"}, new String [] { "addB"});
+        configure(allowList, null, null, new String [] { "defB"}, new String [] { "addB"});
         
         assertAdminLogin("defB", true);
         assertAdminLogin("addB", true);
@@ -116,11 +118,11 @@
     }
     
     @Test
-    public void testRegexpWhitelist() throws ConfigurationException {
+    public void testRegexpAllowList() throws ConfigurationException {
         final String [] allowed = {
                 "bundle3", "bundle4"
         };
-        configure(whitelist, null, "foo.*bar", allowed, null);
+        configure(allowList, null, "foo.*bar", allowed, null);
 
         assertAdminLogin("foo.2.bar", true);
         assertAdminLogin("foo.somethingElse.bar", true);
@@ -136,16 +138,16 @@
 
 
     @Test
-    public void testWhitelistFragment() throws ConfigurationException {
+    public void testAllowListFragment() throws ConfigurationException {
         final String [] allowed1 = randomBsn().toArray(new String[0]);
         final String [] allowed2 = randomBsn().toArray(new String[0]);
 
-        final WhitelistFragment testFragment1 = new WhitelistFragment("test1", allowed1);
-        final WhitelistFragment testFragment2 = new WhitelistFragment("test2", allowed2);
+        final AllowListFragment testFragment1 = new AllowListFragment("test1", allowed1);
+        final AllowListFragment testFragment2 = new AllowListFragment("test2", allowed2);
 
-        configure(whitelist, null, null, null, null);
-        whitelist.bindWhitelistFragment(testFragment1);
-        whitelist.bindWhitelistFragment(testFragment2);
+        configure(allowList, null, null, null, null);
+        allowList.bindAllowListFragment(testFragment1);
+        allowList.bindAllowListFragment(testFragment2);
 
         for(String bsn : allowed1) {
             assertAdminLogin(bsn, true);
@@ -159,7 +161,7 @@
             assertAdminLogin(bsn, false);
         }
 
-        whitelist.unbindWhitelistFragment(testFragment1);
+        allowList.unbindAllowListFragment(testFragment1);
 
         for(String bsn : allowed1) {
             assertAdminLogin(bsn, false);
@@ -170,13 +172,13 @@
         }
     }
 
-    private void configure(final LoginAdminWhitelist whitelist, final Boolean bypass, final String regexp, final String[] defaultBSNs, final String[] additionalBSNs) throws ConfigurationException {
+    private void configure(final LoginAdminAllowList allowList, final Boolean bypass, final String regexp, final String[] defaultBSNs, final String[] additionalBSNs) throws ConfigurationException {
         final Hashtable<String, Object> props = new Hashtable<>();
         if (bypass != null) {
-            props.put("whitelist.bypass", bypass);
+            props.put("allowlist.bypass", bypass);
         }
         if (regexp != null) {
-            props.put("whitelist.bundles.regexp", regexp);
+            props.put("allowlist.bundles.regexp", regexp);
         }
         if (defaultBSNs != null) {
             props.put("whitelist.bundles.default", defaultBSNs);
@@ -184,8 +186,34 @@
         if (additionalBSNs != null) {
             props.put("whitelist.bundles.additional", additionalBSNs);
         }
-        LoginAdminWhitelistConfiguration configuration =
-                ConfigAnnotationUtil.fromDictionary(LoginAdminWhitelistConfiguration.class, props);
-        whitelist.configure(configuration, props);
+        LoginAdminAllowListConfiguration configuration =
+                ConfigAnnotationUtil.fromDictionary(LoginAdminAllowListConfiguration.class, props);
+        allowList.configure(configuration, props);
+    }
+
+    @Test
+    public void testLegacyConfigurationOnly() {
+        final LoginAdminAllowListConfiguration cfg = Mockito.mock(LoginAdminAllowListConfiguration.class);
+        when(cfg.allowlist_bypass()).thenReturn(false);
+        when(cfg.allowlist_bundles_regexp()).thenReturn("");
+        final Hashtable<String, Object> props = new Hashtable<>();
+        props.put("whitelist.bypass", true);
+        props.put("whitelist.bundles.regexp", "foo.*bar");
+        final LoginAdminAllowList.ConfigurationState state = new LoginAdminAllowList.ConfigurationState(cfg, props);
+        assertTrue(state.bypassAllowList);
+        assertEquals("foo.*bar", state.allowListRegexp.pattern());
+    }
+
+    @Test
+    public void testLegacyAndConfiguration() {
+        final LoginAdminAllowListConfiguration cfg = Mockito.mock(LoginAdminAllowListConfiguration.class);
+        when(cfg.allowlist_bypass()).thenReturn(true);
+        when(cfg.allowlist_bundles_regexp()).thenReturn("bar.foo*");
+        final Hashtable<String, Object> props = new Hashtable<>();
+        props.put("whitelist.bypass", false);
+        props.put("whitelist.bundles.regexp", "foo.*bar");
+        final LoginAdminAllowList.ConfigurationState state = new LoginAdminAllowList.ConfigurationState(cfg, props);
+        assertFalse(state.bypassAllowList);
+        assertEquals("bar.foo*", state.allowListRegexp.pattern());
     }
 }
\ No newline at end of file