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

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


The following commit(s) were added to refs/heads/master by this push:
     new 48f7dbf  SLING-12987 Viewing serviceuser details may fail if 
sling:OsgiConfig nodetype is not registered (#6)
48f7dbf is described below

commit 48f7dbfcddab006d88b7e39c3a468969473b2c9f
Author: Eric Norman <[email protected]>
AuthorDate: Tue Nov 25 11:41:04 2025 -0800

    SLING-12987 Viewing serviceuser details may fail if sling:OsgiConfig 
nodetype is not registered (#6)
---
 pom.xml                                            |  12 +-
 .../impl/ServiceUserWebConsolePlugin.java          | 301 +++++++++++++++++----
 .../impl/ServiceUserWebConsolePluginTest.java      | 130 +++++----
 3 files changed, 323 insertions(+), 120 deletions(-)

diff --git a/pom.xml b/pom.xml
index 6dd8eb2..7abf45f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,11 @@
             <artifactId>org.osgi.framework</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.cm</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.service.component</artifactId>
@@ -98,6 +103,11 @@
             <artifactId>org.osgi.service.component.annotations</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.util.converter</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
@@ -154,7 +164,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.sling-mock.junit5</artifactId>
-            <version>3.5.4</version>
+            <version>3.5.6</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git 
a/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java
 
b/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java
index f69b4e7..e9fa119 100644
--- 
a/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java
+++ 
b/src/main/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePlugin.java
@@ -20,7 +20,7 @@ package org.apache.sling.serviceuser.webconsole.impl;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.query.Query;
 import javax.jcr.security.AccessControlEntry;
 import javax.jcr.security.AccessControlList;
@@ -43,11 +43,16 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Dictionary;
 import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.StreamSupport;
 
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.ObjectUtils;
@@ -74,13 +79,19 @@ import org.apache.sling.jcr.base.util.AccessControlUtil;
 import org.apache.sling.serviceusermapping.Mapping;
 import org.apache.sling.serviceusermapping.ServiceUserMapper;
 import org.apache.sling.xss.XSSAPI;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
+import org.osgi.util.converter.Converters;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -98,6 +109,8 @@ import org.slf4j.LoggerFactory;
 @SuppressWarnings("serial")
 public class ServiceUserWebConsolePlugin extends AbstractWebConsolePlugin {
 
+    private static final String PROP_USER_MAPPING = "user.mapping";
+    private static final String NT_SLING_OSGI_CONFIG = "sling:OsgiConfig";
     private static final String TD = "</td>";
     private static final String BR = "<br/>";
     private static final String TR = "</tr>";
@@ -114,6 +127,7 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
     public static final String PN_BUNDLE = "bundle";
     public static final String PN_NAME = "name";
     public static final String PN_SUB_SERVICE = "subService";
+    public static final String PN_CONFIGURATION_INSTANCE_IDENTIFIER = 
"configurationInstanceIdentifier";
     public static final String PN_USER = "user";
     public static final String PN_USER_PATH = "userPath";
 
@@ -127,24 +141,51 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
 
     private final ServiceUserMapper mapper;
 
+    private final ConfigurationAdmin configAdmin;
+
     @Activate
     public ServiceUserWebConsolePlugin(
             ComponentContext context,
             @Reference XSSAPI xss,
             @Reference ResourceResolverFactory resolverFactory,
-            @Reference ServiceUserMapper mapper) {
+            @Reference ServiceUserMapper mapper,
+            @Reference ConfigurationAdmin configAdmin) {
         super();
         this.bundleContext = context.getBundleContext();
         this.xss = xss;
         this.resolverFactory = resolverFactory;
         this.mapper = mapper;
+        this.configAdmin = configAdmin;
     }
 
     private boolean createOrUpdateMapping(HttpServletRequest request, 
ResourceResolver resolver) {
+        String appPath = getParameter(request, PN_APP_PATH, "");
+        // if the appPath was not supplied or the sling:OsgiConfig node type 
is missing
+        //  then delegate to the ConfigurationAdmin apis to decide where to 
store it
+        if (StringUtils.isBlank(appPath) || !hasRegisteredNodeType(resolver, 
NT_SLING_OSGI_CONFIG)) {
+            return createOrUpdateConfigViaConfigAdmin(request);
+        } else {
+            // a specific "appPath" was requested, so fallback to creating the 
configuration
+            // resource manually and wait for the JcrInstaller to detect and 
process it
+            return createOrUpdateConfigViaOsgiConfigResource(request, 
resolver);
+        }
+    }
 
+    /**
+     * Create or update the persisted configuration by creating a 
sling:OsgiConfig resource
+     * under the request app path
+     *
+     * @param request the request to process
+     * @return true if the config resource was persisted, false otherwise
+     */
+    private boolean 
createOrUpdateConfigViaOsgiConfigResource(HttpServletRequest request, 
ResourceResolver resolver) {
         String appPath = getParameter(request, PN_APP_PATH, "");
+        String instanceIdentifier = getParameter(request, 
PN_CONFIGURATION_INSTANCE_IDENTIFIER, "");
+        if (StringUtils.isBlank(instanceIdentifier)) {
+            // no value specified, so fallback to the old way of using the 
last segment of the appPath value
+            instanceIdentifier = appPath.substring(appPath.lastIndexOf('/') + 
1);
+        }
 
-        String instanceIdentifier = appPath.substring(appPath.lastIndexOf('/') 
+ 1);
         String pid = String.format("%s~%s", COMPONENT_NAME, 
instanceIdentifier);
         Iterator<Resource> configs = resolver.findResources(
                 "SELECT * FROM [sling:OsgiConfig] WHERE ISDESCENDANTNODE([" + 
appPath + "]) AND NAME() = '" + pid + "'",
@@ -158,36 +199,21 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
                 config = configs.next();
                 log.debug("Using existing configuration {}", config);
             } else {
-                String path =
-                        appPath + "/config/" + COMPONENT_NAME + "-" + 
appPath.substring(appPath.lastIndexOf('/') + 1);
+                String path = appPath + "/config/" + COMPONENT_NAME + "-" + 
instanceIdentifier;
                 log.debug("Creating new configuration {}", path);
 
                 config = ResourceUtil.getOrCreateResource(
                         resolver,
                         path,
-                        Collections.singletonMap(JcrConstants.JCR_PRIMARYTYPE, 
(Object) "sling:OsgiConfig"),
-                        NodeType.NT_FOLDER,
+                        Collections.singletonMap(JcrConstants.JCR_PRIMARYTYPE, 
(Object) NT_SLING_OSGI_CONFIG),
+                        null,
                         false);
                 dirty = true;
             }
 
-            String bundle = getParameter(request, PN_BUNDLE, "");
-            String subService = getParameter(request, PN_SUB_SERVICE, "");
-            String name = getParameter(request, PN_NAME, "");
-            String mapping = bundle + (StringUtils.isNotBlank(subService) ? 
":" + subService : "") + "=" + name;
-
             ModifiableValueMap properties = 
config.adaptTo(ModifiableValueMap.class);
-            String[] mappings = properties.get("user.mapping", new String[0]);
-            if (!ArrayUtils.contains(mappings, mapping)) {
-                log.debug("Adding {} into service user mapping", mapping);
-                List<String> m = new ArrayList<>();
-                m.addAll(Arrays.asList(mappings));
-                m.add(mapping);
-                properties.put("user.mapping", m.toArray(new 
String[m.size()]));
-                dirty = true;
-            } else {
-                log.debug("Already found {} in service user mapping", mapping);
-            }
+            dirty = updateUserMappingProperty(request, dirty, properties::get, 
properties::put);
+
             if (dirty) {
                 log.debug("Saving changes to osgi config");
                 resolver.commit();
@@ -196,10 +222,90 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
             log.warn("Exception creating service mapping", e);
             return false;
         }
+        return true;
+    }
+
+    /**
+     * Create or update the persisted configuration by delegating to the 
ConfigurationAdmin
+     * apis.
+     *
+     * @param request the request to process
+     * @return true if the config was persisted, false otherwise
+     */
+    private boolean createOrUpdateConfigViaConfigAdmin(HttpServletRequest 
request) {
+        boolean dirty = false;
+        Configuration cfg;
+        String instanceIdentifier = getParameter(request, 
PN_CONFIGURATION_INSTANCE_IDENTIFIER, "");
+        try {
+            cfg = configAdmin.getFactoryConfiguration(COMPONENT_NAME, 
instanceIdentifier, null);
+        } catch (IOException e) {
+            log.warn("Exception getting factory configuration", e);
+            return false;
+        }
+        final String pid = cfg.getPid();
+        Dictionary<String, Object> properties = cfg.getProperties();
+        if (properties != null) {
+            log.debug("Updating existing configuration {}", pid);
+        } else {
+            log.debug("Creating new configuration {}", pid);
+            properties = new Hashtable<>();
+            dirty = true;
+        }
+
+        dirty = updateUserMappingProperty(request, dirty, properties::get, 
properties::put);
+
+        if (dirty) {
+            log.debug("Saving changes to osgi config");
+            try {
+                cfg.update(properties);
+            } catch (IOException e) {
+                log.warn("Exception storing factory configuration", e);
+                return false;
+            }
+        }
 
         return true;
     }
 
+    /**
+     * Updates the user.mapping property if it does not already exist
+     *
+     * @param request the current request
+     * @param dirty specifies if there are changes to be persisted
+     * @param getFn the function to get the current value
+     * @param putFn the function to set the current value
+     * @return true if updates happened or the original dirty argument 
otherwise
+     */
+    private boolean updateUserMappingProperty(
+            HttpServletRequest request,
+            boolean dirty,
+            Function<String, Object> getFn,
+            BiFunction<String, Object, Object> putFn) {
+        String bundle = getParameter(request, PN_BUNDLE, "");
+        String subService = getParameter(request, PN_SUB_SERVICE, "");
+        String name = getParameter(request, PN_NAME, "");
+        String mapping = bundle + (StringUtils.isNotBlank(subService) ? ":" + 
subService : "") + "=[" + name + "]";
+
+        Object mappingsValue = getFn.apply(PROP_USER_MAPPING);
+        String[] mappings;
+        if (mappingsValue instanceof String[]) {
+            mappings = (String[]) mappingsValue;
+        } else {
+            mappings = new String[0];
+        }
+        if (!ArrayUtils.contains(mappings, mapping)) {
+            log.debug("Adding {} into service user mapping", mapping);
+            List<String> m = new ArrayList<>();
+            m.addAll(Arrays.asList(mappings));
+            m.add(mapping);
+            putFn.apply(PROP_USER_MAPPING, m.toArray(new String[m.size()]));
+            dirty = true;
+        } else {
+            log.debug("Already found {} in service user mapping", mapping);
+        }
+        return dirty;
+    }
+
     @Override
     protected void doPost(HttpServletRequest request, HttpServletResponse 
response)
             throws ServletException, IOException {
@@ -207,9 +313,13 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
 
         ResourceResolver resolver = null;
         try {
+            // NOTE: the appPath param can now be blank if the persistence 
location is not specific
             if (StringUtils.isBlank(getParameter(request, PN_NAME, ""))
                     || StringUtils.isBlank(getParameter(request, PN_BUNDLE, 
""))
-                    || StringUtils.isBlank(getParameter(request, PN_APP_PATH, 
""))) {
+                    // NOTE: if an appPath value is supplied then the 
configurationInstanceIdentifier
+                    //    becomes optional since the value can fallback to the 
last segment of the appPath
+                    || (StringUtils.isBlank(getParameter(request, PN_APP_PATH, 
""))
+                            && StringUtils.isBlank(getParameter(request, 
PN_CONFIGURATION_INSTANCE_IDENTIFIER, "")))) {
                 sendErrorRedirect(request, response, "Missing required 
parameters!");
                 return;
             }
@@ -304,29 +414,79 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
         return bundles.get(symbolicName);
     }
 
-    private Object findConfigurations(ResourceResolver resolver, String name, 
List<String> affectedPaths) {
+    /**
+     * Helper to check if the specified node type has been registered
+     *
+     * @param resolver the resolver to check
+     * @param typeName the node type name to check
+     * @return true if the node type exists, false otherwise
+     */
+    private boolean hasRegisteredNodeType(@NotNull ResourceResolver resolver, 
@NotNull String typeName) {
+        boolean hasNodeType = false;
+        final @Nullable Session jcrSession = resolver.adaptTo(Session.class);
+        if (jcrSession != null) {
+            try {
+                final NodeTypeManager nodeTypeManager =
+                        jcrSession.getWorkspace().getNodeTypeManager();
+                hasNodeType = nodeTypeManager.hasNodeType(typeName);
+            } catch (RepositoryException e) {
+                log.warn("Unable to detemine if node type is registered", e);
+            }
+        }
+        return hasNodeType;
+    }
+
+    private String[] findConfigurations(String name, List<String> 
affectedPaths) {
         List<String> configurations = new ArrayList<>();
 
-        Iterator<Resource> configResources = resolver.findResources(
-                "SELECT * FROM [sling:OsgiConfig] AS s WHERE 
(ISDESCENDANTNODE([/apps]) OR ISDESCENDANTNODE([/libs])) AND NAME(s) LIKE 
'org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended%' AND 
[user.mapping] LIKE '%="
-                        + name + "'",
-                Query.JCR_SQL2);
-        while (configResources.hasNext()) {
-            Resource configResource = configResources.next();
-            affectedPaths.add(configResource.getPath());
-            configurations.add(configResource.getPath());
-        }
-        configResources = resolver.findResources(
-                "SELECT * FROM [nt:file] AS s WHERE (ISDESCENDANTNODE([/apps]) 
OR ISDESCENDANTNODE([/libs])) AND NAME(s) LIKE 
'org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended%' AND 
[jcr:content/jcr:data] LIKE '%="
-                        + name + "%'",
-                Query.JCR_SQL2);
-        while (configResources.hasNext()) {
-            Resource configResource = configResources.next();
-            affectedPaths.add(configResource.getPath());
-            configurations.add(configResource.getPath());
+        Configuration[] cfg = null;
+        try {
+            cfg = configAdmin.listConfigurations("(service.factoryPid=" + 
COMPONENT_NAME + "*)");
+        } catch (IOException | InvalidSyntaxException e) {
+            log.warn("Failed to list the configurations", e);
+        }
+
+        // scan the configurations to find any that are mapped to our user
+        if (cfg != null) {
+            for (Configuration configuration : cfg) {
+                final Dictionary<String, Object> properties = 
configuration.getProperties();
+                String[] userMappings = Converters.standardConverter()
+                        .convert(properties.get(PROP_USER_MAPPING))
+                        .defaultValue(new String[0])
+                        .to(String[].class);
+                boolean hasMapName = false;
+                boolean hasMapPrincipal = false;
+                for (String userMapping : userMappings) {
+                    // delegate to the Mapping class to parse the value
+                    final Mapping mapping = new Mapping(userMapping.trim());
+
+                    // check for match in userName variant
+                    final String serviceName = mapping.getServiceName();
+                    final String subServiceName = mapping.getSubServiceName();
+                    hasMapName = name.equals(mapping.map(serviceName, 
subServiceName));
+                    // check for match in principalNames variant
+                    final Iterable<String> mapPrincipals = 
mapping.mapPrincipals(serviceName, subServiceName);
+                    if (mapPrincipals != null) {
+                        hasMapPrincipal = 
StreamSupport.stream(mapPrincipals.spliterator(), false)
+                                .anyMatch(name::equals);
+                    }
+
+                    if (hasMapName || hasMapPrincipal) {
+                        // found a match, so keep track of it
+                        configurations.add(configuration.getPid());
+                        // if this has a jcr install path, add it to the 
affected paths
+                        String jcrConfigPath = (String) 
properties.get("_jcr_config_path");
+                        if (jcrConfigPath != null) {
+                            
affectedPaths.add(jcrConfigPath.substring(jcrConfigPath.indexOf(':') + 1));
+                        }
+                        // found a match, so we can stop looking
+                        break;
+                    }
+                }
+            }
         }
 
-        return configurations.toArray();
+        return configurations.toArray(new String[configurations.size()]);
     }
 
     private String[] findMappings(String name) {
@@ -637,7 +797,16 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
             tableRows(pw);
 
             td(pw, "OSGi Configurations");
-            td(pw, findConfigurations(resolver, name, affectedPaths));
+            pw.print("<td>");
+            String[] findConfigurations = findConfigurations(name, 
affectedPaths);
+            for (String configPid : findConfigurations) {
+                pw.print("<a href='/system/console/configMgr/");
+                pw.print(xss.encodeForHTMLAttr(configPid));
+                pw.print("'>");
+                
pw.print(xss.encodeForHTML(ObjectUtils.defaultIfNull(configPid, "")));
+                pw.print("</a>");
+                pw.println("<br>");
+            }
 
             tableRows(pw);
 
@@ -727,16 +896,37 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
                 "Optional: Allows for different permissions for different 
services within a bundle");
 
         tableRows(pw);
-        String appPath = getParameter(request, PN_APP_PATH, "");
+        String configInstanceIdentifier = getParameter(request, 
PN_CONFIGURATION_INSTANCE_IDENTIFIER, "");
         textField(
                 pw,
-                "Application Path",
-                PN_APP_PATH,
-                appPath,
-                "The application under which to create the OSGi Configuration 
for the Service User Mapping, e.g. /apps/myapp");
-
+                "Configuration Instance Identifier",
+                PN_CONFIGURATION_INSTANCE_IDENTIFIER,
+                configInstanceIdentifier,
+                "The instance idenfitier suffix for the configuration PID");
         tableRows(pw);
 
+        // hide this field if the nodetype from 
org.apache.sling.installer.provider.jcr is missing
+        ResourceResolver resolver = null;
+        try {
+            resolver = getResourceResolver(request);
+
+            if (hasRegisteredNodeType(resolver, NT_SLING_OSGI_CONFIG)) {
+                String appPath = getParameter(request, PN_APP_PATH, "");
+                textField(
+                        pw,
+                        "Application Path",
+                        PN_APP_PATH,
+                        appPath,
+                        "Optional: The application under which to create the 
OSGi Configuration for the Service User Mapping, e.g. /apps/myapp or leave 
empty to delegate to ConfigurationAdmin to decide where to store it");
+
+                tableRows(pw);
+            }
+        } finally {
+            if (needsAdministrativeResolver(request) && resolver != null) {
+                resolver.close();
+            }
+        }
+
         List<Pair<String, String>> privileges = getPrivileges(request);
         printPrivilegeSelect(
                 pw, "ACLs", privileges, getSupportedPrivileges(request), "Set 
the privileges for this service user");
@@ -835,9 +1025,14 @@ public class ServiceUserWebConsolePlugin extends 
AbstractWebConsolePlugin {
     private void sendErrorRedirect(HttpServletRequest request, 
HttpServletResponse response, String alert)
             throws IOException {
         List<String> params = new ArrayList<>();
-        for (String param : new String[] {PN_APP_PATH, PN_BUNDLE, PN_NAME, 
PN_SUB_SERVICE, PN_USER_PATH}) {
-            params.add(param + "="
-                    + URLEncoder.encode(this.getParameter(request, param, ""), 
StandardCharsets.UTF_8.toString()));
+        for (String param : new String[] {
+            PN_CONFIGURATION_INSTANCE_IDENTIFIER, PN_APP_PATH, PN_BUNDLE, 
PN_NAME, PN_SUB_SERVICE, PN_USER_PATH
+        }) {
+            final String parameterValue = this.getParameter(request, param, 
"");
+            // only append the param if it has a non-empty value
+            if (!StringUtils.isEmpty(parameterValue)) {
+                params.add(param + "=" + URLEncoder.encode(parameterValue, 
StandardCharsets.UTF_8.toString()));
+            }
         }
 
         int idx = 0;
diff --git 
a/src/test/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePluginTest.java
 
b/src/test/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePluginTest.java
index ed48bf1..c7f5955 100644
--- 
a/src/test/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePluginTest.java
+++ 
b/src/test/java/org/apache/sling/serviceuser/webconsole/impl/ServiceUserWebConsolePluginTest.java
@@ -62,6 +62,7 @@ import org.apache.sling.jcr.base.util.AccessControlUtil;
 import org.apache.sling.serviceusermapping.impl.MappingConfigAmendment;
 import org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl;
 import org.apache.sling.testing.mock.jcr.MockJcr;
+import org.apache.sling.testing.mock.osgi.MapUtil;
 import org.apache.sling.testing.mock.osgi.MockBundle;
 import org.apache.sling.testing.mock.sling.ResourceResolverType;
 import org.apache.sling.testing.mock.sling.junit5.SlingContext;
@@ -84,6 +85,8 @@ import org.mockito.invocation.InvocationOnMock;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -110,10 +113,11 @@ class ServiceUserWebConsolePluginTest {
      */
     private enum TestConfigOptions {
         PRECREATE_SERVICEUSER,
-        PRECREATE_MAPPING_CONFIG,
+        PRECREATE_MAPPING_CONFIG_RESOURCE,
         PRECREATE_MAPPING_CONFIG_USERMAPPING,
         PRECREATE_ACLS,
-        USE_ADMINISTRATIVE_RESOURCE_RESOLVER
+        USE_ADMINISTRATIVE_RESOURCE_RESOLVER,
+        PRECREATE_CONFIGURATIONS
     }
 
     @BeforeEach
@@ -126,6 +130,11 @@ class ServiceUserWebConsolePluginTest {
      * Test method for {@link 
org.apache.sling.serviceuser.webconsole.impl.ServiceUserWebConsolePlugin#doPost(javax.servlet.http.HttpServletRequest,
 javax.servlet.http.HttpServletResponse)}.
      */
     protected static Stream<Arguments> testDoPostArgs() {
+        Map<String, Object> reqParams0 = new HashMap<>();
+        reqParams0.put(ServiceUserWebConsolePlugin.PN_NAME, "myserviceuser1");
+        reqParams0.put(ServiceUserWebConsolePlugin.PN_BUNDLE, "my.bundle1");
+        
reqParams0.put(ServiceUserWebConsolePlugin.PN_CONFIGURATION_INSTANCE_IDENTIFIER,
 "myapp1");
+
         Map<String, Object> reqParams1 = new HashMap<>();
         reqParams1.put(ServiceUserWebConsolePlugin.PN_NAME, "myserviceuser1");
         reqParams1.put(ServiceUserWebConsolePlugin.PN_BUNDLE, "my.bundle1");
@@ -133,6 +142,7 @@ class ServiceUserWebConsolePluginTest {
 
         Map<String, Object> reqParams2 = new HashMap<>(reqParams1);
         reqParams2.put(ServiceUserWebConsolePlugin.PN_SUB_SERVICE, 
"subservice1");
+        
reqParams2.put(ServiceUserWebConsolePlugin.PN_CONFIGURATION_INSTANCE_IDENTIFIER,
 "myapp1");
 
         Map<String, Object> reqParams3 = new HashMap<>(reqParams1);
         // privilege params
@@ -140,21 +150,24 @@ class ServiceUserWebConsolePluginTest {
         reqParams3.put("acl-privilege-0", "jcr:read");
 
         return Stream.of(
+                Arguments.of(reqParams0, new HashSet<>()),
                 Arguments.of(reqParams1, new HashSet<>()),
                 Arguments.of(
                         reqParams1,
                         new 
HashSet<>(Arrays.asList(TestConfigOptions.USE_ADMINISTRATIVE_RESOURCE_RESOLVER))),
                 Arguments.of(reqParams1, new 
HashSet<>(Arrays.asList(TestConfigOptions.PRECREATE_SERVICEUSER))),
-                Arguments.of(reqParams1, new 
HashSet<>(Arrays.asList(TestConfigOptions.PRECREATE_MAPPING_CONFIG))),
+                Arguments.of(
+                        reqParams1, new 
HashSet<>(Arrays.asList(TestConfigOptions.PRECREATE_MAPPING_CONFIG_RESOURCE))),
                 Arguments.of(
                         reqParams1,
                         new HashSet<>(Arrays.asList(
-                                TestConfigOptions.PRECREATE_SERVICEUSER, 
TestConfigOptions.PRECREATE_MAPPING_CONFIG))),
+                                TestConfigOptions.PRECREATE_SERVICEUSER,
+                                
TestConfigOptions.PRECREATE_MAPPING_CONFIG_RESOURCE))),
                 Arguments.of(
                         reqParams2,
                         new HashSet<>(Arrays.asList(
                                 TestConfigOptions.PRECREATE_SERVICEUSER,
-                                TestConfigOptions.PRECREATE_MAPPING_CONFIG,
+                                
TestConfigOptions.PRECREATE_MAPPING_CONFIG_RESOURCE,
                                 
TestConfigOptions.PRECREATE_MAPPING_CONFIG_USERMAPPING))),
                 Arguments.of(reqParams3, new 
HashSet<>(Arrays.asList(TestConfigOptions.PRECREATE_ACLS))));
     }
@@ -174,7 +187,7 @@ class ServiceUserWebConsolePluginTest {
             ((JackrabbitSession) 
jcrSession).getUserManager().createSystemUser(name, null);
         }
 
-        if (options.contains(TestConfigOptions.PRECREATE_MAPPING_CONFIG)) {
+        if 
(options.contains(TestConfigOptions.PRECREATE_MAPPING_CONFIG_RESOURCE)) {
             // create the mapping and mock the jcr query
             String appPath = 
request.getParameter(ServiceUserWebConsolePlugin.PN_APP_PATH);
             String identifier = appPath.substring(appPath.lastIndexOf('/') + 
1);
@@ -578,7 +591,10 @@ class ServiceUserWebConsolePluginTest {
         return Stream.of(
                 Arguments.of(new HashSet<>()),
                 Arguments.of(new 
HashSet<>(Arrays.asList(TestConfigOptions.USE_ADMINISTRATIVE_RESOURCE_RESOLVER))),
-                Arguments.of(new 
HashSet<>(Arrays.asList(TestConfigOptions.PRECREATE_SERVICEUSER))));
+                Arguments.of(new HashSet<>(Arrays.asList(
+                        TestConfigOptions.PRECREATE_CONFIGURATIONS,
+                        TestConfigOptions.PRECREATE_ACLS,
+                        TestConfigOptions.PRECREATE_SERVICEUSER))));
     }
 
     @ParameterizedTest
@@ -600,38 +616,47 @@ class ServiceUserWebConsolePluginTest {
         final ResourceResolver rr = request.getResourceResolver();
         final @Nullable Session jcrSession = rr.adaptTo(Session.class);
 
-        // provide some "OSGi Configurations" to print in the output and mock 
the queries
-        String mapping1 =
-                
toUserMappingValue(bundleContext.getBundle().getSymbolicName(), "subservice1", 
"myserviceuser1");
-        Resource config1 = createOsgiConfig(
-                rr,
-                
"/apps/sling/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~test1",
-                mapping1);
-        MockJcr.setQueryResult(
-                jcrSession,
-                "SELECT * FROM [sling:OsgiConfig] AS s WHERE 
(ISDESCENDANTNODE([/apps]) OR ISDESCENDANTNODE([/libs])) AND NAME(s) LIKE 
'org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended%' AND 
[user.mapping] LIKE '%=myserviceuser1'",
-                Query.JCR_SQL2,
-                Arrays.asList(config1.adaptTo(Node.class)));
-        // also the alternate code path where the config is stored as a 
nt:file resource
-        String mapping2 =
-                
toUserMappingValue(bundleContext.getBundle().getSymbolicName(), "subservice2", 
"myserviceuser1");
-        Resource config2 = createFileConfig(
-                rr,
-                
"/apps/sling/install/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~test2",
-                mapping2);
-        MockJcr.setQueryResult(
-                jcrSession,
-                "SELECT * FROM [nt:file] AS s WHERE (ISDESCENDANTNODE([/apps]) 
OR ISDESCENDANTNODE([/libs])) AND NAME(s) LIKE 
'org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended%' AND 
[jcr:content/jcr:data] LIKE '%=myserviceuser1%'",
-                Query.JCR_SQL2,
-                Arrays.asList(config2.adaptTo(Node.class)));
-
-        // provide some "ACLs" to print in the output and mock the query
-        Resource node1 = createNodeWithAce(rr, "/content/node1", 
"myserviceuser1");
-        MockJcr.setQueryResult(
-                jcrSession,
-                "SELECT * FROM [rep:GrantACE] AS s WHERE  [rep:principalName] 
= 'myserviceuser1'",
-                Query.JCR_SQL2,
-                Arrays.asList(node1.adaptTo(Node.class)));
+        if (options.contains(TestConfigOptions.PRECREATE_CONFIGURATIONS)) {
+            // provide some "OSGi Configurations" to print in the output and 
mock the queries
+            ConfigurationAdmin configAdmin = 
context.getService(ConfigurationAdmin.class);
+            Configuration config1 =
+                    
configAdmin.getFactoryConfiguration(ServiceUserWebConsolePlugin.COMPONENT_NAME, 
"test1", null);
+            HashMap<String, Object> props1 = new HashMap<>();
+            String mapping1 =
+                    
toUserMappingValue(bundleContext.getBundle().getSymbolicName(), "subservice1", 
"myserviceuser1");
+            props1.put("user.mapping", new String[] {mapping1});
+            // simulate a config loaded by jcrinstall
+            props1.put(
+                    "_jcr_config_path",
+                    
"jcrinstall:/libs/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~test1.pid");
+            config1.update(MapUtil.toDictionary(props1));
+
+            Configuration config2 =
+                    
configAdmin.getFactoryConfiguration(ServiceUserWebConsolePlugin.COMPONENT_NAME, 
"test2", null);
+            HashMap<String, Object> props2 = new HashMap<>();
+            String mapping2 = toUserMappingValue(
+                    bundleContext.getBundle().getSymbolicName(), 
"subservice1", null, Arrays.asList("myserviceuser1"));
+            props2.put("user.mapping", new String[] {mapping2});
+            config2.update(MapUtil.toDictionary(props2));
+
+            Configuration config3 =
+                    
configAdmin.getFactoryConfiguration(ServiceUserWebConsolePlugin.COMPONENT_NAME, 
"test3", null);
+            HashMap<String, Object> props3 = new HashMap<>();
+            String mapping3 = toUserMappingValue(
+                    bundleContext.getBundle().getSymbolicName(), 
"subservice1", null, Arrays.asList("myserviceuser2"));
+            props3.put("user.mapping", new String[] {mapping3});
+            config3.update(MapUtil.toDictionary(props3));
+        }
+
+        if (options.contains(TestConfigOptions.PRECREATE_ACLS)) {
+            // provide some "ACLs" to print in the output and mock the query
+            Resource node1 = createNodeWithAce(rr, "/content/node1", 
"myserviceuser1");
+            MockJcr.setQueryResult(
+                    jcrSession,
+                    "SELECT * FROM [rep:GrantACE] AS s WHERE  
[rep:principalName] = 'myserviceuser1'",
+                    Query.JCR_SQL2,
+                    Arrays.asList(node1.adaptTo(Node.class)));
+        }
 
         if (options.contains(TestConfigOptions.PRECREATE_SERVICEUSER)) {
             ((JackrabbitSession) 
jcrSession).getUserManager().createSystemUser("myserviceuser1", null);
@@ -817,33 +842,6 @@ class ServiceUserWebConsolePluginTest {
         return config;
     }
 
-    /**
-     * Creates a nt:file configuration resource
-     *
-     * @param rr the resource resolver
-     * @param path the path for the cresource
-     * @param mapping (optional) the mapping to apply
-     * @return
-     * @throws PersistenceException
-     */
-    private @NotNull Resource createFileConfig(
-            final @NotNull ResourceResolver rr, @NotNull String path, 
@Nullable String mapping)
-            throws PersistenceException {
-        Resource config = ResourceUtil.getOrCreateResource(
-                rr, path, 
Collections.singletonMap(JcrConstants.JCR_PRIMARYTYPE, "nt:file"), 
NodeType.NT_FOLDER, false);
-
-        Map<String, Object> properties = new HashMap<>();
-        if (mapping != null) {
-            List<String> m = new ArrayList<>();
-            m.add(mapping);
-            properties.put("jcr:data", m.toArray(new String[m.size()]));
-        }
-        rr.create(config, "jcr:content", properties);
-        rr.commit();
-
-        return config;
-    }
-
     /**
      * Creates a folder resource with an ACL pre-created
      *


Reply via email to