This is an automated email from the ASF dual-hosted git repository.

enapps-enorman pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-extensions-webconsolesecurityprovider.git


The following commit(s) were added to refs/heads/master by this push:
     new 14bccdf  SLING-13181 migrate to org.apache.felix.webconsole 5.x and 
jakarta Servlet (#6)
14bccdf is described below

commit 14bccdfbe82a5b8d9da56e64a972058f3675d4b9
Author: Eric Norman <[email protected]>
AuthorDate: Mon May 4 15:21:25 2026 -0700

    SLING-13181 migrate to org.apache.felix.webconsole 5.x and jakarta Servlet 
(#6)
---
 bnd.bnd                                            |   4 +
 pom.xml                                            |  65 +++++-
 .../AbstractWebConsoleSecurityProvider.java        |  87 --------
 .../internal/Activator.java                        |   5 +-
 .../{Activator.java => ConfigConstants.java}       |  41 ++--
 .../internal/ServicesListener.java                 |  32 +--
 .../internal/SlingWebConsoleSecurityProvider.java  |  68 ++++--
 .../internal/SlingWebConsoleSecurityProvider2.java | 135 +++++++-----
 .../internal/ActivatorTest.java                    |  63 ++++++
 .../internal/ServiceListenerTest.java              |  60 ++----
 .../SlingWebConsoleSecurityProvider2Test.java      | 228 +++++++++++++++++++++
 .../SlingWebConsoleSecurityProviderTest.java       | 153 ++++++++++++++
 12 files changed, 687 insertions(+), 254 deletions(-)

diff --git a/bnd.bnd b/bnd.bnd
index 40a6f22..2a155f5 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -10,3 +10,7 @@ DynamicImport-Package:\
   org.apache.sling.api.auth;version="[1.0,2)",\
   org.apache.sling.api.resource;version="[2.3,3)",\
   org.apache.sling.auth.core;version="[1.0,2)"
+
+# make a copy of private classes that we need
+-includeresource:\
+  
@org.apache.felix.webconsole-*.jar!/org/apache/felix/webconsole/internal/servlet/(BasicWebConsoleSecurityProvider*)
diff --git a/pom.xml b/pom.xml
index 6724557..e051149 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,6 +45,12 @@
     </properties>
 
     <dependencies>
+        <!-- JetBrains annotations for null-analysis (SLING-7798) -->
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.framework</artifactId>
@@ -55,22 +61,37 @@
             <artifactId>org.osgi.service.cm</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.http.whiteboard</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.util.converter</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.webconsole</artifactId>
-            <version>4.2.10</version>
+            <version>5.0.10</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.4.0</version>
+            <version>3.0.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.auth.core</artifactId>
-            <version>1.0.0</version>
+            <version>2.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -85,8 +106,7 @@
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
-            <artifactId>jackrabbit-api</artifactId>
-            <version>2.6.0</version>
+            <artifactId>oak-jackrabbit-api</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -103,7 +123,21 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.osgi-mock.junit4</artifactId>
-            <version>3.3.0</version>
+            <version>3.5.8</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- need a newer jcr-mock version than the indirect one from 
sling-mock for SLING-13177 fix.
+             this may be removed if sling-mock updates to this version or 
later-->
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.jcr-mock</artifactId>
+            <version>1.8.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId>
+            <version>4.0.6</version>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -134,4 +168,23 @@
             </plugin>
         </plugins>
     </build>
+    <profiles>
+        <profile>
+            <id>jacoco-report</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.jacoco</groupId>
+                        <artifactId>jacoco-maven-plugin</artifactId>
+                        <configuration>
+                            <!-- exclude the binary classes that were copied 
from elsewhere -->
+                            <excludes>
+                                
<exclude>org/apache/felix/webconsole/internal/servlet/*</exclude>
+                            </excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
 </project>
diff --git 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/AbstractWebConsoleSecurityProvider.java
 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/AbstractWebConsoleSecurityProvider.java
deleted file mode 100644
index 3595c0e..0000000
--- 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/AbstractWebConsoleSecurityProvider.java
+++ /dev/null
@@ -1,87 +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.extensions.webconsolesecurityprovider.internal;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.felix.webconsole.WebConsoleSecurityProvider;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This is the common base class for the two provider implementations.
- * It handles the configuration of the service.
- */
-public abstract class AbstractWebConsoleSecurityProvider implements 
WebConsoleSecurityProvider, ManagedService {
-
-    // name of the property providing list of authorized users
-    private static final String PROP_USERS = "users";
-
-    // default user being authorized
-    public static final String PROP_GROUPS_DEFAULT_USER = "admin";
-
-    // name of the property providing list of groups whose members are
-    // authorized
-    private static final String PROP_GROUPS = "groups";
-
-    /** default logger */
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-
-    protected Set<String> users = 
Collections.singleton(PROP_GROUPS_DEFAULT_USER);
-
-    protected Set<String> groups = Collections.emptySet();
-
-    /**
-     * Handle configuration
-     * @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary)
-     */
-    @Override
-    public void updated(final Dictionary<String, ?> properties) throws 
ConfigurationException {
-        this.users = toSet(properties == null ? null : 
properties.get(PROP_USERS), PROP_GROUPS_DEFAULT_USER);
-        this.groups = toSet(properties == null ? null : 
properties.get(PROP_GROUPS), null);
-    }
-
-    private Set<String> toSet(final Object configObj, final String 
defaultUser) {
-        final Set<String> groups = new HashSet<String>();
-        if (configObj instanceof String) {
-            groups.add((String) configObj);
-        } else if (configObj instanceof Collection<?>) {
-            for (Object obj : ((Collection<?>) configObj)) {
-                if (obj instanceof String) {
-                    groups.add((String) obj);
-                }
-            }
-        } else if (configObj instanceof String[]) {
-            for (String string : ((String[]) configObj)) {
-                if (string != null) {
-                    groups.add(string);
-                }
-            }
-        } else if (configObj == null && defaultUser != null) {
-            groups.add(defaultUser);
-        }
-        return groups;
-    }
-}
diff --git 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/Activator.java
 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/Activator.java
index 127203c..59e37ca 100644
--- 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/Activator.java
+++ 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/Activator.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sling.extensions.webconsolesecurityprovider.internal;
 
+import org.jetbrains.annotations.NotNull;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 
@@ -29,7 +30,7 @@ public class Activator implements BundleActivator {
      * @see 
org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
      */
     @Override
-    public void start(final BundleContext context) throws Exception {
+    public void start(@NotNull final BundleContext context) throws Exception {
         listener = new ServicesListener(context);
     }
 
@@ -37,7 +38,7 @@ public class Activator implements BundleActivator {
      * @see 
org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
      */
     @Override
-    public void stop(final BundleContext context) throws Exception {
+    public void stop(@NotNull final BundleContext context) throws Exception {
         if (listener != null) {
             listener.deactivate();
             listener = null;
diff --git 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/Activator.java
 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ConfigConstants.java
similarity index 55%
copy from 
src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/Activator.java
copy to 
src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ConfigConstants.java
index 127203c..ab5286f 100644
--- 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/Activator.java
+++ 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ConfigConstants.java
@@ -18,29 +18,28 @@
  */
 package org.apache.sling.extensions.webconsolesecurityprovider.internal;
 
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
+import java.util.Collections;
+import java.util.Set;
 
-public class Activator implements BundleActivator {
-
-    private ServicesListener listener;
+/**
+ * This is the common constants for the two provider implementations.
+ */
+class ConfigConstants {
 
-    /**
-     * @see 
org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
-     */
-    @Override
-    public void start(final BundleContext context) throws Exception {
-        listener = new ServicesListener(context);
+    private ConfigConstants() {
+        /* This utility class should not be instantiated */
     }
 
-    /**
-     * @see 
org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
-     */
-    @Override
-    public void stop(final BundleContext context) throws Exception {
-        if (listener != null) {
-            listener.deactivate();
-            listener = null;
-        }
-    }
+    // name of the property providing list of authorized users
+    static final String PROP_USERS = "users";
+
+    // default user being authorized
+    static final Set<String> PROP_DEFAULT_USERS = Set.of("admin");
+
+    // name of the property providing list of groups whose members are
+    // authorized
+    static final String PROP_GROUPS = "groups";
+
+    // default user being authorized
+    static final Set<String> PROP_DEFAULT_GROUPS = Collections.emptySet();
 }
diff --git 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServicesListener.java
 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServicesListener.java
index 708f8da..5478849 100644
--- 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServicesListener.java
+++ 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServicesListener.java
@@ -17,29 +17,11 @@
  * under the License.
  */
 package org.apache.sling.extensions.webconsolesecurityprovider.internal;
-/*
- * 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.
- */
 
 import java.util.Dictionary;
 import java.util.Hashtable;
 
-import org.apache.felix.webconsole.WebConsoleSecurityProvider;
+import org.apache.felix.webconsole.spi.SecurityProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
@@ -60,7 +42,7 @@ import org.slf4j.LoggerFactory;
  *   <li> "jcrAuth": always authenticate against the JCR repository even if 
Sling Authentication is possible.</li>
  *   <li> "slingAuth": always use SlingAuthentication
  *   <li> no value (default) : Use SlingAuthentication if available, fallback 
to JCR repository
- *   <li> If an invalid value is specifed, the value is ignored and the 
default is used
+ *   <li> If an invalid value is specified, the value is ignored and the 
default is used
  * </ul>
  */
 public class ServicesListener {
@@ -202,26 +184,26 @@ public class ServicesListener {
     }
 
     private void registerProviderSling(final Object authSupport, final Object 
authenticator) {
-        final Dictionary<String, Object> props = new Hashtable<String, 
Object>();
+        final Dictionary<String, Object> props = new Hashtable<>();
         props.put(Constants.SERVICE_PID, 
SlingWebConsoleSecurityProvider.class.getName());
         props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Web Console 
Security Provider 2");
         props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
         props.put("webconsole.security.provider.id", 
"org.apache.sling.extensions.webconsolesecurityprovider2");
         this.provider2Reg = this.bundleContext.registerService(
-                new String[] {ManagedService.class.getName(), 
WebConsoleSecurityProvider.class.getName()},
+                new String[] {ManagedService.class.getName(), 
SecurityProvider.class.getName()},
                 new SlingWebConsoleSecurityProvider2(authSupport, 
authenticator),
                 props);
     }
 
     private void registerProviderJcr(final Object repository) {
-        final Dictionary<String, Object> props = new Hashtable<String, 
Object>();
+        final Dictionary<String, Object> props = new Hashtable<>();
         props.put(Constants.SERVICE_PID, 
SlingWebConsoleSecurityProvider.class.getName());
         props.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Web Console 
Security Provider");
         props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
         props.put("webconsole.security.provider.id", 
"org.apache.sling.extensions.webconsolesecurityprovider");
         this.providerReg = this.bundleContext.registerService(
-                new String[] {ManagedService.class.getName(), 
WebConsoleSecurityProvider.class.getName()},
-                new SlingWebConsoleSecurityProvider(repository),
+                new String[] {ManagedService.class.getName(), 
SecurityProvider.class.getName()},
+                new SlingWebConsoleSecurityProvider(this.bundleContext, 
repository),
                 props);
     }
 
diff --git 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider.java
 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider.java
index 1b91b38..db8d0a8 100644
--- 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider.java
+++ 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider.java
@@ -24,13 +24,30 @@ import javax.jcr.Repository;
 import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
 
+import java.util.Dictionary;
 import java.util.Iterator;
+import java.util.Set;
 
+import 
org.apache.felix.webconsole.internal.servlet.BasicWebConsoleSecurityProvider;
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.Group;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.api.security.user.UserManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.util.converter.Converter;
+import org.osgi.util.converter.Converters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_DEFAULT_GROUPS;
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_DEFAULT_USERS;
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_GROUPS;
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_USERS;
 
 /**
  * The <code>SlingWebConsoleSecurityProvider</code> is security provider for 
the
@@ -44,15 +61,39 @@ import org.apache.jackrabbit.api.security.user.UserManager;
  * only registered as a security provider service once such a JCR Repository is
  * available.
  */
-public class SlingWebConsoleSecurityProvider extends 
AbstractWebConsoleSecurityProvider {
+public class SlingWebConsoleSecurityProvider extends 
BasicWebConsoleSecurityProvider implements ManagedService {
+
+    /** default logger */
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+    protected Set<String> users = PROP_DEFAULT_USERS;
+    protected Set<String> groups = PROP_DEFAULT_GROUPS;
 
     private Repository repository;
 
-    public SlingWebConsoleSecurityProvider(final Object repository) {
+    public SlingWebConsoleSecurityProvider(
+            @NotNull final BundleContext bundleContext, @NotNull final Object 
repository) {
+        super(bundleContext);
         this.repository = (Repository) repository;
     }
 
-    // ---------- SCR integration
+    /**
+     * Handle configuration
+     * @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary)
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void updated(@Nullable Dictionary<String, ?> properties) throws 
ConfigurationException {
+        final Converter converter = Converters.standardConverter();
+        this.users = converter
+                .convert(properties == null ? null : 
properties.get(PROP_USERS))
+                .defaultValue(PROP_DEFAULT_USERS)
+                .to(Set.class);
+        this.groups = converter
+                .convert(properties == null ? null : 
properties.get(PROP_GROUPS))
+                .defaultValue(PROP_DEFAULT_GROUPS)
+                .to(Set.class);
+    }
 
     /**
      * Authenticates and authorizes the user identified by the user name and
@@ -77,26 +118,25 @@ public class SlingWebConsoleSecurityProvider extends 
AbstractWebConsoleSecurityP
      * @param password The password to authenticate the user. This may be
      *            <code>null</code> to assume an empty password.
      * @return The <code>userName</code> is currently returned to indicate
-     *         successfull authentication.
+     *         successful authentication.
      * @throws NullPointerException if <code>userName</code> is
      *             <code>null</code>.
      */
     @Override
-    public Object authenticate(String userName, String password) {
+    public @Nullable Object authenticate(@NotNull String userName, @Nullable 
String password) {
         final Credentials creds =
                 new SimpleCredentials(userName, (password == null) ? new 
char[0] : password.toCharArray());
         Session session = null;
         try {
             session = repository.login(creds);
-            if (session instanceof JackrabbitSession) {
-                UserManager umgr = ((JackrabbitSession) 
session).getUserManager();
+            if (session instanceof JackrabbitSession jrSession) {
+                UserManager umgr = jrSession.getUserManager();
                 String userId = session.getUserID();
                 Authorizable a = umgr.getAuthorizable(userId);
                 if (a instanceof User) {
-
                     // check users
                     if (users.contains(userId)) {
-                        return true;
+                        return userName;
                     }
 
                     // check groups
@@ -135,14 +175,4 @@ public class SlingWebConsoleSecurityProvider extends 
AbstractWebConsoleSecurityP
         // no success (see log)
         return null;
     }
-
-    /**
-     * All users authenticated with the repository and being a member of the
-     * authorized groups are granted access for all roles in the Web Console.
-     */
-    @Override
-    public boolean authorize(Object user, String role) {
-        logger.debug("authorize: Grant user {} access for role {}", user, 
role);
-        return true;
-    }
 }
diff --git 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2.java
 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2.java
index 30be211..bf8aaea 100644
--- 
a/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2.java
+++ 
b/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2.java
@@ -20,12 +20,14 @@ package 
org.apache.sling.extensions.webconsolesecurityprovider.internal;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 
+import java.util.Dictionary;
 import java.util.Iterator;
+import java.util.Set;
 
-import org.apache.felix.webconsole.WebConsoleSecurityProvider3;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.felix.webconsole.spi.SecurityProvider;
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.api.security.user.Group;
@@ -34,6 +36,19 @@ import org.apache.jackrabbit.api.security.user.UserManager;
 import org.apache.sling.api.auth.Authenticator;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.auth.core.AuthenticationSupport;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.util.converter.Converter;
+import org.osgi.util.converter.Converters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_DEFAULT_GROUPS;
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_DEFAULT_USERS;
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_GROUPS;
+import static 
org.apache.sling.extensions.webconsolesecurityprovider.internal.ConfigConstants.PROP_USERS;
 
 /**
  * The <code>SlingWebConsoleSecurityProvider</code> is security provider for 
the
@@ -47,77 +62,102 @@ import org.apache.sling.auth.core.AuthenticationSupport;
  * only registered as a security provider service once such a JCR Repository is
  * available.
  */
-public class SlingWebConsoleSecurityProvider2 extends 
AbstractWebConsoleSecurityProvider
-        implements WebConsoleSecurityProvider3 {
+public class SlingWebConsoleSecurityProvider2 implements SecurityProvider, 
ManagedService {
+
+    /** default logger */
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+    protected Set<String> users = PROP_DEFAULT_USERS;
+    protected Set<String> groups = PROP_DEFAULT_GROUPS;
 
     private final AuthenticationSupport authentiationSupport;
 
     private final Authenticator authenticator;
 
-    public SlingWebConsoleSecurityProvider2(final Object support, final Object 
authenticator) {
+    public SlingWebConsoleSecurityProvider2(@NotNull final Object support, 
@NotNull final Object authenticator) {
         this.authentiationSupport = (AuthenticationSupport) support;
         this.authenticator = (Authenticator) authenticator;
     }
 
     /**
-     * @see 
org.apache.felix.webconsole.WebConsoleSecurityProvider2#authenticate(javax.servlet.http.HttpServletRequest,
 javax.servlet.http.HttpServletResponse)
+     * Handle configuration
+     * @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary)
      */
+    @SuppressWarnings("unchecked")
     @Override
-    public boolean authenticate(final HttpServletRequest request, final 
HttpServletResponse response) {
-        if (this.authentiationSupport.handleSecurity(request, response)) {
-            // get ResourceResolver (set by AuthenticationSupport)
-            Object resolverObject = 
request.getAttribute(AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER);
-            final ResourceResolver resolver =
-                    (resolverObject instanceof ResourceResolver) ? 
(ResourceResolver) resolverObject : null;
-            if (resolver != null) {
-                final Session session = resolver.adaptTo(Session.class);
-                if (session != null) {
-                    try {
-                        final User u = this.authenticate(session);
-                        if (u != null) {
-                            request.setAttribute(USER_ATTRIBUTE, u);
-                            return true;
-                        }
-                    } catch (final Exception re) {
-                        logger.info(
-                                "authenticate: Generic problem trying grant 
User " + " access to the Web Console", re);
-                    }
-                }
-            }
-            if (request.getAuthType() == null) {
-                this.authenticator.login(request, response);
-            }
-        }
-        return false;
+    public void updated(@Nullable Dictionary<String, ?> properties) throws 
ConfigurationException {
+        final Converter converter = Converters.standardConverter();
+        this.users = converter
+                .convert(properties == null ? null : 
properties.get(PROP_USERS))
+                .defaultValue(PROP_DEFAULT_USERS)
+                .to(Set.class);
+        this.groups = converter
+                .convert(properties == null ? null : 
properties.get(PROP_GROUPS))
+                .defaultValue(PROP_DEFAULT_GROUPS)
+                .to(Set.class);
     }
 
+    /**
+     * All users authenticated with the repository and being a member of the
+     * authorized groups are granted access for all roles in the Web Console.
+     */
     @Override
-    public void logout(HttpServletRequest request, HttpServletResponse 
response) {
-        this.authenticator.logout(request, response);
+    public boolean authorize(@NotNull Object user, @NotNull String role) {
+        logger.debug("authorize: Grant user {} access for role {}", user, 
role);
+        return true;
     }
 
+    /**
+     * @see 
org.apache.felix.webconsole.spi.SecurityProvider#authenticate(jakarta.servlet.http.HttpServletRequest,
 jakarta.servlet.http.HttpServletResponse)
+     */
     @Override
-    public User authenticate(String userName, String password) {
-        return null; // this method is never invoked
+    public @Nullable Object authenticate(
+            @NotNull final HttpServletRequest request, @NotNull final 
HttpServletResponse response) {
+        try {
+            if (this.authentiationSupport.handleSecurity(request, response)) {
+                // get ResourceResolver (set by AuthenticationSupport)
+                Object resolverObject = 
request.getAttribute(AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER);
+                if (resolverObject instanceof ResourceResolver resolver) {
+                    final Session session = resolver.adaptTo(Session.class);
+                    if (session != null) {
+                        try {
+                            final User u = this.authenticate(session);
+                            if (u != null) {
+                                return u.getID();
+                            }
+                        } catch (final Exception re) {
+                            logger.info(
+                                    "authenticate: Generic problem trying 
grant User access to the Web Console", re);
+                        }
+                    }
+                }
+                if (request.getAuthType() == null) {
+                    this.authenticator.login(request, response);
+                }
+            }
+        } finally {
+            this.authentiationSupport.finishSecurity(request, response);
+        }
+        return null;
     }
 
-    private User authenticate(final Session session) throws 
RepositoryException {
+    protected @Nullable User authenticate(@NotNull final Session session) 
throws RepositoryException {
         String userId = session.getUserID();
-        if (session instanceof JackrabbitSession) {
-            UserManager umgr = ((JackrabbitSession) session).getUserManager();
+        if (session instanceof JackrabbitSession jrSession) {
+            UserManager umgr = jrSession.getUserManager();
             Authorizable a = umgr.getAuthorizable(userId);
-            if (a instanceof User) {
+            if (a instanceof User u) {
 
                 // check users
                 if (users.contains(userId)) {
-                    return (User) a;
+                    return u;
                 }
 
                 // check groups
                 Iterator<Group> gi = a.memberOf();
                 while (gi.hasNext()) {
                     if (groups.contains(gi.next().getID())) {
-                        return (User) a;
+                        return u;
                     }
                 }
 
@@ -134,13 +174,8 @@ public class SlingWebConsoleSecurityProvider2 extends 
AbstractWebConsoleSecurity
         return null;
     }
 
-    /**
-     * All users authenticated with the repository and being a member of the
-     * authorized groups are granted access for all roles in the Web Console.
-     */
     @Override
-    public boolean authorize(Object user, String role) {
-        logger.debug("authorize: Grant user {} access for role {}", user, 
role);
-        return true;
+    public void logout(@NotNull HttpServletRequest request, @NotNull 
HttpServletResponse response) {
+        this.authenticator.logout(request, response);
     }
 }
diff --git 
a/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ActivatorTest.java
 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ActivatorTest.java
new file mode 100644
index 0000000..7019bc5
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ActivatorTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.extensions.webconsolesecurityprovider.internal;
+
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.Test.None;
+import org.osgi.framework.BundleContext;
+
+/**
+ *
+ */
+public class ActivatorTest {
+
+    @Rule
+    public OsgiContext context = new OsgiContext();
+
+    private Activator activator = new Activator();
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.Activator#start(org.osgi.framework.BundleContext)}.
+     */
+    @Test(expected = None.class)
+    public void testStart() throws Exception {
+        final @NotNull BundleContext bundleContext = context.bundleContext();
+        activator.start(bundleContext);
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.Activator#stop(org.osgi.framework.BundleContext)}.
+     */
+    @Test(expected = None.class)
+    public void testStop() throws Exception {
+        final @NotNull BundleContext bundleContext = context.bundleContext();
+
+        // start
+        activator.start(bundleContext);
+
+        // stop
+        activator.stop(bundleContext);
+
+        // stop one more time for code coverage
+        activator.stop(bundleContext);
+    }
+}
diff --git 
a/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServiceListenerTest.java
 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServiceListenerTest.java
index 322d154..e2df3b2 100644
--- 
a/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServiceListenerTest.java
+++ 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/ServiceListenerTest.java
@@ -17,28 +17,10 @@
  * under the License.
  */
 package org.apache.sling.extensions.webconsolesecurityprovider.internal;
-/*
- * 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.
- */
 
 import javax.jcr.Repository;
 
-import org.apache.felix.webconsole.WebConsoleSecurityProvider;
+import org.apache.felix.webconsole.spi.SecurityProvider;
 import org.apache.sling.api.auth.Authenticator;
 import org.apache.sling.auth.core.AuthenticationSupport;
 import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
@@ -47,9 +29,7 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.osgi.framework.BundleContext;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -78,12 +58,14 @@ public class ServiceListenerTest {
 
     @After
     public void shutdown() {
-        listener.deactivate();
+        if (listener != null) {
+            listener.deactivate();
+        }
     }
 
     @Test
     public void testDefaultAuth() {
-        listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+        listener = new ServicesListener(context.bundleContext());
         assertNoSecurityProviderRegistered();
 
         context.registerService(Repository.class, repository);
@@ -103,7 +85,7 @@ public class ServiceListenerTest {
     public void testWithSlingAuth() {
         try {
             System.setProperty(ServicesListener.WEBCONSOLE_AUTH_TYPE, 
ServicesListener.SLING_AUTH);
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertNoSecurityProviderRegistered();
 
             context.registerService(Repository.class, repository);
@@ -126,7 +108,7 @@ public class ServiceListenerTest {
     public void testWithForcedJcrAuth() {
         try {
             System.setProperty(ServicesListener.WEBCONSOLE_AUTH_TYPE, 
ServicesListener.JCR_AUTH);
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertNoSecurityProviderRegistered();
 
             // no matter what is registered, always the auth against the repo 
needs to be there
@@ -150,19 +132,19 @@ public class ServiceListenerTest {
     @Test
     public void testGetAuthType() {
         try {
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertEquals(ServicesListener.AuthType.DEFAULT, 
listener.getAuthType());
 
             System.setProperty(ServicesListener.WEBCONSOLE_AUTH_TYPE, 
ServicesListener.JCR_AUTH);
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertEquals(ServicesListener.AuthType.JCR, 
listener.getAuthType());
 
             System.setProperty(ServicesListener.WEBCONSOLE_AUTH_TYPE, 
ServicesListener.SLING_AUTH);
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertEquals(ServicesListener.AuthType.SLING, 
listener.getAuthType());
 
             System.setProperty(ServicesListener.WEBCONSOLE_AUTH_TYPE, 
"invalid");
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertEquals(ServicesListener.AuthType.DEFAULT, 
listener.getAuthType());
         } finally {
             
System.getProperties().remove(ServicesListener.WEBCONSOLE_AUTH_TYPE);
@@ -172,21 +154,21 @@ public class ServiceListenerTest {
     @Test
     public void testGetTargetState() {
         try {
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertEquals(ServicesListener.State.NONE, 
listener.getTargetState(false, false));
             assertEquals(ServicesListener.State.PROVIDER_JCR, 
listener.getTargetState(false, true));
             assertEquals(ServicesListener.State.PROVIDER_SLING, 
listener.getTargetState(true, false));
             assertEquals(ServicesListener.State.PROVIDER_SLING, 
listener.getTargetState(true, true));
 
             System.setProperty(ServicesListener.WEBCONSOLE_AUTH_TYPE, 
ServicesListener.JCR_AUTH);
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertEquals(ServicesListener.State.NONE, 
listener.getTargetState(false, false));
             assertEquals(ServicesListener.State.PROVIDER_JCR, 
listener.getTargetState(false, true));
             assertEquals(ServicesListener.State.NONE, 
listener.getTargetState(true, false));
             assertEquals(ServicesListener.State.PROVIDER_JCR, 
listener.getTargetState(true, true));
 
             System.setProperty(ServicesListener.WEBCONSOLE_AUTH_TYPE, 
ServicesListener.SLING_AUTH);
-            listener = new 
ServicesListener(wrapForValidProperties(context.bundleContext()));
+            listener = new ServicesListener(context.bundleContext());
             assertEquals(ServicesListener.State.NONE, 
listener.getTargetState(false, false));
             assertEquals(ServicesListener.State.NONE, 
listener.getTargetState(false, true));
             assertEquals(ServicesListener.State.PROVIDER_SLING, 
listener.getTargetState(true, false));
@@ -196,16 +178,6 @@ public class ServiceListenerTest {
         }
     }
 
-    // until https://issues.apache.org/jira/browse/SLING-11505 is implemented
-    private BundleContext wrapForValidProperties(BundleContext bc) {
-        BundleContext spy = Mockito.spy(bc);
-        
Mockito.when(spy.getProperty(Mockito.anyString())).thenAnswer(invocation -> {
-            String key = (String) invocation.getArguments()[0];
-            return System.getProperty(key);
-        });
-        return spy;
-    }
-
     // Helpers
 
     private void assertRepositoryRegistered() {
@@ -224,7 +196,7 @@ public class ServiceListenerTest {
         assertNull(getSecurityProvider());
     }
 
-    private WebConsoleSecurityProvider getSecurityProvider() {
-        return context.getService(WebConsoleSecurityProvider.class);
+    private SecurityProvider getSecurityProvider() {
+        return context.getService(SecurityProvider.class);
     }
 }
diff --git 
a/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2Test.java
 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2Test.java
new file mode 100644
index 0000000..6254cc0
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2Test.java
@@ -0,0 +1,228 @@
+/*
+ * 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.extensions.webconsolesecurityprovider.internal;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.sling.api.auth.Authenticator;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.auth.core.AuthenticationSupport;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import 
org.apache.sling.testing.mock.sling.servlet.MockSlingJakartaHttpServletRequest;
+import 
org.apache.sling.testing.mock.sling.servlet.MockSlingJakartaHttpServletResponse;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.osgi.service.cm.ConfigurationException;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.withSettings;
+
+/**
+ *
+ */
+public class SlingWebConsoleSecurityProvider2Test {
+
+    @Rule
+    public SlingContext context = new 
SlingContext(ResourceResolverType.JCR_MOCK);
+
+    private SlingWebConsoleSecurityProvider2 provider;
+
+    private AuthenticationSupport support;
+    private Authenticator authenticator;
+
+    @Before
+    public void before() {
+        support = Mockito.mock(AuthenticationSupport.class);
+        authenticator = Mockito.mock(Authenticator.class);
+        provider = new SlingWebConsoleSecurityProvider2(support, 
authenticator);
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.SlingWebConsoleSecurityProvider2#updated(java.util.Dictionary)}.
+     */
+    @Test
+    public void testUpdated() throws ConfigurationException {
+        provider.updated(null);
+        assertEquals(ConfigConstants.PROP_DEFAULT_USERS, provider.users);
+        assertEquals(ConfigConstants.PROP_DEFAULT_GROUPS, provider.groups);
+
+        provider.updated(new Hashtable<>(Map.of()));
+        assertEquals(ConfigConstants.PROP_DEFAULT_USERS, provider.users);
+        assertEquals(ConfigConstants.PROP_DEFAULT_GROUPS, provider.groups);
+
+        provider.updated(new Hashtable<>(Map.of("users", "user1", "groups", 
"group1")));
+        assertEquals(Set.of("user1"), provider.users);
+        assertEquals(Set.of("group1"), provider.groups);
+
+        provider.updated(new Hashtable<>(
+                Map.of("users", new String[] {"user1", "user2"}, "groups", new 
String[] {"group1", "group2"})));
+        assertEquals(Set.of("user1", "user2"), provider.users);
+        assertEquals(Set.of("group1", "group2"), provider.groups);
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.SlingWebConsoleSecurityProvider2#authenticate(jakarta.servlet.http.HttpServletRequest,
 jakarta.servlet.http.HttpServletResponse)}.
+     */
+    @Test
+    public void testAuthenticate() throws RepositoryException, 
ConfigurationException {
+        final @NotNull MockSlingJakartaHttpServletRequest request = 
context.jakartaRequest();
+        final @NotNull MockSlingJakartaHttpServletResponse response = 
context.jakartaResponse();
+
+        // security not handled
+        Mockito.doReturn(false)
+                .when(support)
+                .handleSecurity(any(HttpServletRequest.class), 
any(HttpServletResponse.class));
+        assertNull(provider.authenticate(request, response));
+
+        // security handled without ResourceResolver (set by 
AuthenticationSupport)
+        Mockito.doReturn(true)
+                .when(support)
+                .handleSecurity(any(HttpServletRequest.class), 
any(HttpServletResponse.class));
+        assertNull(provider.authenticate(request, response));
+
+        // security handled with ResourceResolver (set by 
AuthenticationSupport)
+        final @NotNull ResourceResolver rr = context.resourceResolver();
+        request.setAttribute(AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER, 
rr);
+        // for code coverage simulate no auth challenge path
+        request.setAuthType("FORMS");
+        assertNull(provider.authenticate(request, response));
+
+        // for code coverage simulate auth challenge path
+        request.setAuthType(null);
+        assertNull(provider.authenticate(request, response));
+
+        // create a user
+        UserManager userManager = ((JackrabbitSession) 
rr.adaptTo(Session.class)).getUserManager();
+        final @NotNull User user1 = userManager.createUser("admin", "test1");
+
+        // user does exist, and in the configuration
+        assertEquals("admin", provider.authenticate(request, response));
+
+        // change the users in the configuration
+        provider.updated(new Hashtable<>(Map.of("users", "test1")));
+
+        // user does exist, but not in the configuration
+        assertNull(provider.authenticate(request, response));
+
+        // add user to a group
+        final @NotNull Group group1 = userManager.createGroup("group1");
+        group1.addMember(user1);
+        final @NotNull Group group2 = userManager.createGroup("group2");
+
+        // change the users in the configuration
+        provider.updated(new Hashtable<>(Map.of("users", "test1", "groups", 
"group2")));
+
+        // user does exist, but not in the groups configuration
+        assertNull(provider.authenticate(request, response));
+
+        // add to the configured group
+        group2.addMember(user1);
+        // user does exist, and in the groups configuration
+        assertEquals("admin", provider.authenticate(request, response));
+    }
+
+    @Test
+    public void testAuthenticateWithNoSession() {
+        final @NotNull MockSlingJakartaHttpServletRequest request = 
context.jakartaRequest();
+        final @NotNull MockSlingJakartaHttpServletResponse response = 
context.jakartaResponse();
+
+        // security handled
+        Mockito.doReturn(true)
+                .when(support)
+                .handleSecurity(any(HttpServletRequest.class), 
any(HttpServletResponse.class));
+
+        // security handled with ResourceResolver (set by 
AuthenticationSupport)
+        final @NotNull ResourceResolver rr = 
Mockito.spy(context.resourceResolver());
+        request.setAttribute(AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER, 
rr);
+
+        // simulate an null session
+        Mockito.doReturn(null).when(rr).adaptTo(Session.class);
+
+        // expected no user resolved and null returned
+        assertNull(provider.authenticate(request, response));
+    }
+
+    @Test
+    public void testAuthenticateWithCaughtException() throws 
RepositoryException {
+        final @NotNull MockSlingJakartaHttpServletRequest request = 
context.jakartaRequest();
+        final @NotNull MockSlingJakartaHttpServletResponse response = 
context.jakartaResponse();
+
+        // security handled
+        Mockito.doReturn(true)
+                .when(support)
+                .handleSecurity(any(HttpServletRequest.class), 
any(HttpServletResponse.class));
+
+        // security handled with ResourceResolver (set by 
AuthenticationSupport)
+        final @NotNull ResourceResolver rr = 
Mockito.spy(context.resourceResolver());
+        request.setAttribute(AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER, 
rr);
+
+        // simulate an exception thrown
+        final Session mockSession =
+                Mockito.mock(Session.class, 
withSettings().extraInterfaces(JackrabbitSession.class));
+        Mockito.doThrow(UnsupportedRepositoryOperationException.class)
+                .when((JackrabbitSession) mockSession)
+                .getUserManager();
+        Mockito.doReturn(mockSession).when(rr).adaptTo(Session.class);
+
+        // expected exception caught, message logged and null returned
+        assertNull(provider.authenticate(request, response));
+    }
+
+    @Test
+    public void testAuthenticateWithoutJackrabbitSession() throws 
RepositoryException {
+        final Session mockSession = Mockito.mock(Session.class);
+        Mockito.doReturn("test1").when(mockSession).getUserID();
+        assertNull(provider.authenticate(mockSession));
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.SlingWebConsoleSecurityProvider2#logout(jakarta.servlet.http.HttpServletRequest,
 jakarta.servlet.http.HttpServletResponse)}.
+     */
+    @Test(expected = org.junit.Test.None.class)
+    public void testLogout() {
+        final @NotNull MockSlingJakartaHttpServletRequest request = 
context.jakartaRequest();
+        final @NotNull MockSlingJakartaHttpServletResponse response = 
context.jakartaResponse();
+        provider.logout(request, response);
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.SlingWebConsoleSecurityProvider2#authorize(java.lang.Object,
 java.lang.String)}.
+     */
+    @Test
+    public void testAuthorize() {
+        assertTrue(provider.authorize("testuser1", "role1"));
+    }
+}
diff --git 
a/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProviderTest.java
 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProviderTest.java
new file mode 100644
index 0000000..e4db003
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProviderTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.extensions.webconsolesecurityprovider.internal;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.osgi.service.cm.ConfigurationException;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.any;
+
+/**
+ *
+ */
+public class SlingWebConsoleSecurityProviderTest {
+
+    @Rule
+    public SlingContext context = new 
SlingContext(ResourceResolverType.JCR_MOCK);
+
+    private Repository mockRepo;
+    private SlingWebConsoleSecurityProvider provider;
+
+    @Before
+    public void before() {
+        mockRepo = 
Mockito.spy(context.resourceResolver().adaptTo(Session.class).getRepository());
+        provider = new 
SlingWebConsoleSecurityProvider(context.bundleContext(), mockRepo);
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.SlingWebConsoleSecurityProvider#updated(java.util.Dictionary)}.
+     */
+    @Test
+    public void testUpdated() throws ConfigurationException {
+        provider.updated(null);
+        assertEquals(ConfigConstants.PROP_DEFAULT_USERS, provider.users);
+        assertEquals(ConfigConstants.PROP_DEFAULT_GROUPS, provider.groups);
+
+        provider.updated(new Hashtable<>(Map.of()));
+        assertEquals(ConfigConstants.PROP_DEFAULT_USERS, provider.users);
+        assertEquals(ConfigConstants.PROP_DEFAULT_GROUPS, provider.groups);
+
+        provider.updated(new Hashtable<>(Map.of("users", "user1", "groups", 
"group1")));
+        assertEquals(Set.of("user1"), provider.users);
+        assertEquals(Set.of("group1"), provider.groups);
+
+        provider.updated(new Hashtable<>(
+                Map.of("users", new String[] {"user1", "user2"}, "groups", new 
String[] {"group1", "group2"})));
+        assertEquals(Set.of("user1", "user2"), provider.users);
+        assertEquals(Set.of("group1", "group2"), provider.groups);
+    }
+
+    /**
+     * Test method for {@link 
org.apache.sling.extensions.webconsolesecurityprovider.internal.SlingWebConsoleSecurityProvider#authenticate(java.lang.String,
 java.lang.String)}.
+     */
+    @Test
+    public void testAuthenticate() throws RepositoryException, 
ConfigurationException {
+        // user does not exist
+        assertNull(provider.authenticate("test1", "test1"));
+
+        // null password for code coverage
+        assertNull(provider.authenticate("test1", null));
+
+        // create a user
+        UserManager userManager = ((JackrabbitSession) 
mockRepo.login()).getUserManager();
+        userManager.createUser("test1", "test1");
+
+        // user does exist, but not in the configuration
+        assertNull(provider.authenticate("test1", "test1"));
+
+        // add the user to the configuration
+        provider.updated(new Hashtable<>(Map.of("users", "test1")));
+
+        // user does exist, and also in the configuration
+        assertEquals("test1", provider.authenticate("test1", "test1"));
+
+        // a second user who is in a group
+        final @NotNull User user2 = userManager.createUser("test2", "test2");
+        final @NotNull Group group1 = userManager.createGroup("group1");
+        group1.addMember(user2);
+
+        // user does exist and a not a member of a configured group
+        assertNull(provider.authenticate("test2", "test2"));
+
+        // add the group to the configuration
+        provider.updated(new Hashtable<>(Map.of("groups", "group1")));
+
+        // user does exist and a member of a configured group
+        assertEquals("test2", provider.authenticate("test2", "test2"));
+    }
+
+    @Test
+    public void testAuthenticateWithoutJackrabbitSession() throws 
RepositoryException {
+        // simulate the login return some non-jackrabbit session impl
+        final Session mockSession = Mockito.mock(Session.class);
+        
Mockito.doReturn(mockSession).when(mockRepo).login(any(Credentials.class));
+
+        // user does not exist
+        assertNull(provider.authenticate("test1", "test1"));
+    }
+
+    @Test
+    public void testAuthenticateWithLoginException() throws 
RepositoryException {
+        // simulate the login throwing an exception
+        
Mockito.doThrow(LoginException.class).when(mockRepo).login(any(Credentials.class));
+
+        // user does not exist
+        assertNull(provider.authenticate("test1", "test1"));
+    }
+
+    @Test
+    public void testAuthenticateWithOtherException() throws 
RepositoryException {
+        // simulate the login throwing an exception
+        
Mockito.doThrow(RuntimeException.class).when(mockRepo).login(any(Credentials.class));
+
+        // user does not exist
+        assertNull(provider.authenticate("test1", "test1"));
+    }
+}

Reply via email to