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

cziegeler pushed a commit to branch SLING-9622
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-core.git


The following commit(s) were added to refs/heads/SLING-9622 by this push:
     new 45fff72  SLING-9622 : Handle mapping within auth.core bundle and 
update auth requirements when mapping changes
45fff72 is described below

commit 45fff72707a83ec252c24ad550acd2d06cd0cfec
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Wed Aug 5 08:58:35 2020 +0200

    SLING-9622 : Handle mapping within auth.core bundle and update auth 
requirements when mapping changes
---
 pom.xml                                            |  12 +-
 .../core/impl/AuthenticationHandlerHolder.java     |   6 +-
 .../sling/auth/core/impl/PathBasedHolder.java      |  10 +-
 .../sling/auth/core/impl/PathBasedHolderCache.java | 143 +++++++++------------
 .../sling/auth/core/impl/SlingAuthenticator.java   |   4 +-
 .../impl/SlingAuthenticatorServiceListener.java    |  66 +++++++---
 .../SlingAuthenticatorServiceListenerTest.java     |  69 +++++++++-
 7 files changed, 193 insertions(+), 117 deletions(-)

diff --git a/pom.xml b/pom.xml
index f7d1a1a..97d5350 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,9 +75,15 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.commons.osgi</artifactId>
-            <version>2.2.0</version>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.util.function</artifactId>
+            <version>1.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.util.converter</artifactId>
+            <version>1.0.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git 
a/src/main/java/org/apache/sling/auth/core/impl/AuthenticationHandlerHolder.java
 
b/src/main/java/org/apache/sling/auth/core/impl/AuthenticationHandlerHolder.java
index 066362b..e7ea528 100644
--- 
a/src/main/java/org/apache/sling/auth/core/impl/AuthenticationHandlerHolder.java
+++ 
b/src/main/java/org/apache/sling/auth/core/impl/AuthenticationHandlerHolder.java
@@ -28,8 +28,8 @@ import org.apache.sling.auth.core.AuthUtil;
 import org.apache.sling.auth.core.spi.AuthenticationFeedbackHandler;
 import org.apache.sling.auth.core.spi.AuthenticationHandler;
 import org.apache.sling.auth.core.spi.AuthenticationInfo;
-import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.osgi.framework.ServiceReference;
+import org.osgi.util.converter.Converters;
 
 /**
  * The <code>AuthenticationHandlerHolder</code> class represents an
@@ -53,11 +53,11 @@ final class AuthenticationHandlerHolder extends
             final ServiceReference serviceReference) {
         super(fullPath, serviceReference);
 
-        final String browserOnly = 
PropertiesUtil.toString(serviceReference.getProperty(AuthConstants.AUTH_HANDLER_BROWSER_ONLY),
 null);
+        final String browserOnly = 
Converters.standardConverter().convert(serviceReference.getProperty(AuthConstants.AUTH_HANDLER_BROWSER_ONLY)).to(String.class);
 
         // assign the fields
         this.handler = handler;
-        this.authType = 
PropertiesUtil.toString(serviceReference.getProperty(TYPE_PROPERTY), null);
+        this.authType = 
Converters.standardConverter().convert(serviceReference.getProperty(TYPE_PROPERTY)).to(String.class);
         this.browserOnlyRequestCredentials = 
"true".equalsIgnoreCase(browserOnly)
             || "yes".equalsIgnoreCase(browserOnly);
     }
diff --git a/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolder.java 
b/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolder.java
index 2fc09eb..8d605b1 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolder.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolder.java
@@ -18,9 +18,9 @@
  */
 package org.apache.sling.auth.core.impl;
 
-import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
+import org.osgi.util.converter.Converters;
 
 /**
  * The <code>PathBasedHolder</code> provides the basic abstraction for managing
@@ -135,15 +135,13 @@ public abstract class PathBasedHolder implements 
Comparable<PathBasedHolder> {
             return "Apache Sling Request Authenticator";
         }
 
-        final String descr = PropertiesUtil.toString(
-            serviceReference.getProperty(Constants.SERVICE_DESCRIPTION), null);
+        final String descr = 
Converters.standardConverter().convert(serviceReference.getProperty(Constants.SERVICE_DESCRIPTION)).to(String.class);
         if (descr != null) {
             return descr;
         }
 
-        return "Service "
-            + PropertiesUtil.toString(
-                serviceReference.getProperty(Constants.SERVICE_ID), "unknown");
+        final String id = 
Converters.standardConverter().convert(serviceReference.getProperty(Constants.SERVICE_ID)).defaultValue("unknown").to(String.class);
+        return "Service ".concat(id);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java 
b/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java
index ab0982d..e8def27 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/PathBasedHolderCache.java
@@ -20,120 +20,95 @@ package org.apache.sling.auth.core.impl;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.http.HttpServletRequest;
 
 public class PathBasedHolderCache<Type extends PathBasedHolder> {
 
-    private final Map<String, Map<String, SortedSet<Type>>> cache = new 
HashMap<String, Map<String, SortedSet<Type>>>();
-
-    /** Read/write lock to synchronize the cache access. */
-    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
+    /**
+     * The cache is a concurrent map of concurrent maps.
+     * As the final value, the sorted set is replaced on each change, reading 
of the
+     * cache does not need to be synchronized. Updating of the cache is 
synchronized.
+     */
+    private final Map<String, Map<String, SortedSet<Type>>> cache = new 
ConcurrentHashMap<>();
 
     public void clear() {
-        this.rwLock.writeLock().lock();
-        try {
-            cache.clear();
-        } finally {
-            this.rwLock.writeLock().unlock();
-        }
+        cache.clear();
     }
 
-    public void addHolder(final Type holder) {
-        this.rwLock.writeLock().lock();
-        try {
-
-            final Map<String, SortedSet<Type>> byHostMap = 
cache.computeIfAbsent(holder.protocol, protocol -> new HashMap<>());
+    public synchronized void addHolder(final Type holder) {
+        final Map<String, SortedSet<Type>> byHostMap = 
cache.computeIfAbsent(holder.protocol, protocol -> new ConcurrentHashMap<>());
 
-            final SortedSet<Type> byPathSet = new TreeSet<Type>();
+        final SortedSet<Type> byPathSet = new TreeSet<Type>();
 
-            // preset with current list
-            final SortedSet<Type> currentPathSet = byHostMap.get(holder.host);
-            if (currentPathSet != null) {
-                byPathSet.addAll(currentPathSet);
-            }
+        // preset with current list
+        final SortedSet<Type> currentPathSet = byHostMap.get(holder.host);
+        if (currentPathSet != null) {
+            byPathSet.addAll(currentPathSet);
+        }
 
-            // add the new holder
-            byPathSet.add(holder);
+        // add the new holder
+        byPathSet.add(holder);
 
-            // replace old set with new set
-            byHostMap.put(holder.host, byPathSet);
-        } finally {
-            this.rwLock.writeLock().unlock();
-        }
+        // replace old set with new set
+        byHostMap.put(holder.host, byPathSet);
     }
 
-    public void removeHolder(final Type holder) {
-        this.rwLock.writeLock().lock();
-        try {
-            final Map<String, SortedSet<Type>> byHostMap = 
cache.get(holder.protocol);
-            if (byHostMap != null) {
-                final SortedSet<Type> byPathSet = byHostMap.get(holder.host);
-                if (byPathSet != null) {
-
-                    // create a new set without the removed holder
-                    final SortedSet<Type> set = new TreeSet<Type>();
-                    set.addAll(byPathSet);
-                    set.remove(holder);
-
-                    // replace the old set with the new one (or remove if 
empty)
-                    if (set.isEmpty()) {
-                        byHostMap.remove(holder.host);
-                    } else {
-                        byHostMap.put(holder.host, set);
-                    }
+    public synchronized void removeHolder(final Type holder) {
+        final Map<String, SortedSet<Type>> byHostMap = 
cache.get(holder.protocol);
+        if (byHostMap != null) {
+            final SortedSet<Type> byPathSet = byHostMap.get(holder.host);
+            if (byPathSet != null) {
+
+                // create a new set without the removed holder
+                final SortedSet<Type> set = new TreeSet<Type>();
+                set.addAll(byPathSet);
+                set.remove(holder);
+
+                // replace the old set with the new one (or remove if empty)
+                if (set.isEmpty()) {
+                    byHostMap.remove(holder.host);
+                } else {
+                    byHostMap.put(holder.host, set);
                 }
             }
-        } finally {
-            this.rwLock.writeLock().unlock();
         }
     }
 
     public Collection<Type>[] findApplicableHolders(final HttpServletRequest 
request) {
-        this.rwLock.readLock().lock();
-        try {
-            final String hostname = request.getServerName()
-                  + (request.getServerPort() != 80 && request.getServerPort() 
!= 443
-                    ? ":" + request.getServerPort()
-                    : "");
-
-            @SuppressWarnings("unchecked")
-            final SortedSet<Type>[] result = new SortedSet[4];
-
-            final Map<String, SortedSet<Type>> byHostMap = 
cache.get(request.getScheme());
-            if ( byHostMap != null ) {
-                result[0] = byHostMap.get(hostname);
-                result[1] = byHostMap.get("");
-            }
-            final Map<String, SortedSet<Type>> defaultByHostMap = 
cache.get("");
-            if ( defaultByHostMap != null ) {
-                result[2] = defaultByHostMap.get(hostname);
-                result[3] = defaultByHostMap.get("");
-            }
-            return result;
-        } finally {
-            this.rwLock.readLock().unlock();
+        final String hostname = request.getServerName()
+              + (request.getServerPort() != 80 && request.getServerPort() != 
443
+                ? ":" + request.getServerPort()
+                : "");
+
+        @SuppressWarnings("unchecked")
+        final SortedSet<Type>[] result = new SortedSet[4];
+
+        final Map<String, SortedSet<Type>> byHostMap = 
cache.get(request.getScheme());
+        if ( byHostMap != null ) {
+            result[0] = byHostMap.get(hostname);
+            result[1] = byHostMap.get("");
         }
+        final Map<String, SortedSet<Type>> defaultByHostMap = cache.get("");
+        if ( defaultByHostMap != null ) {
+            result[2] = defaultByHostMap.get(hostname);
+            result[3] = defaultByHostMap.get("");
+        }
+        return result;
     }
 
     public List<Type> getHolders() {
-        this.rwLock.readLock().lock();
-        try {
-            final List<Type> result = new ArrayList<Type>();
-            for (Map<String, SortedSet<Type>> byHostEntry : cache.values()) {
-                for (SortedSet<Type> holderSet : byHostEntry.values()) {
-                    result.addAll(holderSet);
-                }
+        final List<Type> result = new ArrayList<Type>();
+        for (Map<String, SortedSet<Type>> byHostEntry : cache.values()) {
+            for (SortedSet<Type> holderSet : byHostEntry.values()) {
+                result.addAll(holderSet);
             }
-            return result;
-        } finally {
-            this.rwLock.readLock().unlock();
         }
+        return result;
     }
 }
diff --git 
a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java 
b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
index 914099c..6ad51bd 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
@@ -60,7 +60,6 @@ import org.apache.sling.auth.core.spi.AuthenticationHandler;
 import org.apache.sling.auth.core.spi.AuthenticationInfo;
 import org.apache.sling.auth.core.spi.AuthenticationInfoPostProcessor;
 import org.apache.sling.auth.core.spi.DefaultAuthenticationFeedbackHandler;
-import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
@@ -85,6 +84,7 @@ import org.osgi.service.metatype.annotations.AttributeType;
 import org.osgi.service.metatype.annotations.Designate;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 import org.osgi.service.metatype.annotations.Option;
+import org.osgi.util.converter.Converters;
 import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -1720,7 +1720,7 @@ public class SlingAuthenticator implements Authenticator,
         }
 
         private void bindAuthHandler(final Object handler, final 
ServiceReference ref) {
-            final String paths[] = 
PropertiesUtil.toStringArray(ref.getProperty(AuthenticationHandler.PATH_PROPERTY));
+            final String paths[] = 
Converters.standardConverter().convert(ref.getProperty(AuthenticationHandler.PATH_PROPERTY)).to(String[].class);
             if (paths != null && paths.length > 0) {
 
                 // generate the holders
diff --git 
a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListener.java
 
b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListener.java
index ca967f6..46e0958 100644
--- 
a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListener.java
+++ 
b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListener.java
@@ -36,7 +36,6 @@ import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.mapping.ResourceMapper;
 import org.apache.sling.auth.core.AuthConstants;
-import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.osgi.framework.AllServiceListener;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -47,6 +46,7 @@ import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.event.Event;
 import org.osgi.service.event.EventConstants;
 import org.osgi.service.event.EventHandler;
+import org.osgi.util.converter.Converters;
 
 /**
  * Service listener keeping track of all auth requirements registered in
@@ -107,6 +107,12 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
         return null;
     }
 
+    /**
+     * Create a new listener
+     * @param authRequiredCache The cache
+     * @param executor For updating
+     * @param factory The resource resolver factory
+     */
     private SlingAuthenticatorServiceListener(final 
PathBasedHolderCache<AuthenticationRequirementHolder> authRequiredCache,
             final Executor executor,
             final ResourceResolverFactory factory) {
@@ -135,6 +141,9 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
         }
     }
 
+    /**
+     * Handle service registration updates (add, modified, remove)
+     */
     @Override
     public void serviceChanged(final ServiceEvent event) {
         // modification of service properties, unregistration of the
@@ -158,6 +167,9 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
         schedule();
     }
 
+    /**
+     * Handle a mapping event
+     */
     @Override
     public void handleEvent(final Event event) {
         queue(UPDATE, null);
@@ -184,6 +196,10 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
         }
     }
 
+    /**
+     * Process the queue, one by one
+     * Lazy creation of resource resolver / resource mapper
+     */
     private void processQueue() {
         ResourceResolver resolver = null;
         ResourceMapper mapper = null;
@@ -223,11 +239,20 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
         }
     }
 
+    /**
+     * Process a single action
+     * @param mapper
+     * @param id
+     * @param action
+     */
     private void process(final ResourceMapper mapper, final Long id, final 
Action action) {
         switch ( action.type ) {
-            case ADDED : this.addService(mapper, action.reference); break;
-            case REMOVED : 
this.removeService((Long)action.reference.getProperty(Constants.SERVICE_ID)); 
break;
-            case MODIFIED : this.modifiedService(mapper, action.reference); 
break;
+            case ADDED : this.addService(mapper, action.reference);
+                         break;
+            case REMOVED : 
this.removeService((Long)action.reference.getProperty(Constants.SERVICE_ID));
+                           break;
+            case MODIFIED : this.modifiedService(mapper, action.reference);
+                            break;
             case UPDATE: final List<AuthenticationRequirementHolder> list = 
props.get(id);
                          if (!list.isEmpty() ) {
                              this.modifiedService(mapper, 
list.get(0).serviceReference);
@@ -261,17 +286,20 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
                 authReq = authReq.trim();
                 if ( authReq.length() > 0 ) {
                     final String prefix;
-                    if ( authReq.startsWith("+") || authReq.startsWith("-") ) {
-                        prefix = authReq.substring(0, 1);
+                    if ( authReq.startsWith("+") ) {
+                        prefix = null;
+                        authReq = authReq.substring(1);
+                    } else if ( authReq.startsWith("-") ) {
+                        prefix = "-";
+                        authReq = authReq.substring(1);
                     } else {
-                        prefix = "+";
-                        authReq = prefix.concat(authReq);
+                        prefix = null;
                     }
-                    paths.add(authReq);
+                    paths.add(prefix == null ? authReq : 
prefix.concat(authReq));
 
                     if ( mapper != null ) {
-                        for(final String mappedPath : 
mapper.getAllMappings(authReq.substring(1))) {
-                            paths.add(prefix.concat(mappedPath));
+                        for(final String mappedPath : 
mapper.getAllMappings(authReq)) {
+                            paths.add(prefix == null ? mappedPath : 
prefix.concat(mappedPath));
                         }
                     }
                 }
@@ -285,8 +313,8 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
      * @param ref The service reference
      */
     private void addService(final ResourceMapper mapper, final 
ServiceReference<?> ref) {
-        final String[] authReqPaths = 
PropertiesUtil.toStringArray(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS));
-        if ( authReqPaths != null ) {
+        final String[] authReqPaths = 
Converters.standardConverter().convert(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS)).to(String[].class);
+        if ( authReqPaths.length > 0 ) {
             final Long id = (Long)ref.getProperty(Constants.SERVICE_ID);
             final Set<String> paths = buildPathsSet(mapper, authReqPaths);
 
@@ -309,9 +337,9 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
      * @param ref The service reference
      */
     private void modifiedService(final ResourceMapper mapper, final 
ServiceReference<?> ref) {
-        final String[] authReqPaths = 
PropertiesUtil.toStringArray(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS));
+        final String[] authReqPaths = 
Converters.standardConverter().convert(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS)).to(String[].class);
         final Long id = (Long)ref.getProperty(Constants.SERVICE_ID);
-        if ( authReqPaths != null ) {
+        if ( authReqPaths.length > 0 ) {
             final Set<String> oldPaths = regProps.get(id);
             if ( oldPaths == null ) {
                 addService(mapper, ref);
@@ -360,10 +388,18 @@ public class SlingAuthenticatorServiceListener implements 
AllServiceListener, Ev
         regProps.remove(id);
     }
 
+    /**
+     * Action type for the queued execution
+     *
+     */
     public enum ActionType {
         ADDED, MODIFIED, REMOVED, UPDATE
     }
 
+    /**
+     * Action for the queued execution
+     *
+     */
     public static final class Action {
 
         public final ActionType type;
diff --git 
a/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListenerTest.java
 
b/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListenerTest.java
index feffc93..c87a1bc 100644
--- 
a/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListenerTest.java
+++ 
b/src/test/java/org/apache/sling/auth/core/impl/SlingAuthenticatorServiceListenerTest.java
@@ -51,9 +51,9 @@ public class SlingAuthenticatorServiceListenerTest {
             final String[] paths,
             final ServiceReference<?>[] refs,
             final boolean[] requireAuth) {
-        assertEquals(paths.length, refs.length);
+        assertEquals("Wrong input to assert paths", paths.length, refs.length);
         if ( requireAuth != null ) {
-            assertEquals(paths.length, requireAuth.length);
+            assertEquals("Wrong input to assert paths", paths.length, 
requireAuth.length);
         }
 
         assertEquals(paths.length, cache.getHolders().size());
@@ -119,16 +119,46 @@ public class SlingAuthenticatorServiceListenerTest {
         final ServiceReference<?> ref = createServiceReference(new String[] 
{"/path1"});
         listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, 
ref));
 
-        assertEquals(1, cache.getHolders().size());
         assertPaths(cache, new String[] {"/path1"},
                            new ServiceReference<?>[] {ref});
-        assertEquals(ref, cache.getHolders().get(0).serviceReference);
 
         listener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, 
ref));
 
         assertTrue(cache.getHolders().isEmpty());
     }
 
+    @Test public void testAddUpdateRemoveRegistration() throws LoginException {
+        final PathBasedHolderCache<AuthenticationRequirementHolder> cache = 
new PathBasedHolderCache<AuthenticationRequirementHolder>();
+        final BundleContext context = mock(BundleContext.class);
+        final ResourceMapper mapper = mock(ResourceMapper.class);
+        
when(mapper.getAllMappings("/path1")).thenReturn(Arrays.asList("/path1", 
"/path1a"));
+        
when(mapper.getAllMappings("/path2")).thenReturn(Arrays.asList("/path2", 
"/path2a"));
+        
when(mapper.getAllMappings("/path3")).thenReturn(Arrays.asList("/path3", 
"/path3a"));
+
+        final SlingAuthenticatorServiceListener listener = 
SlingAuthenticatorServiceListener.createListener(context, callable -> 
callable.run(), createFactoryForMapper(mapper), cache);
+
+        // add
+        final ServiceReference<?> ref = createServiceReference(new String[] 
{"/path1", "/path2"});
+        listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, 
ref));
+
+        assertPaths(cache, new String[] {"/path1", "/path1a", "/path2", 
"/path2a"},
+                           new ServiceReference<?>[] {ref, ref, ref, ref},
+                           new boolean[] {true, true, true, true});
+
+        // update
+        when(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS)).thenReturn(new 
String[] {"/path2", "/path3"});
+        listener.serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, ref));
+
+        assertPaths(cache, new String[] {"/path2", "/path2a", "/path3", 
"/path3a"},
+                new ServiceReference<?>[] {ref, ref, ref, ref},
+                new boolean[] {true, true, true, true});
+
+        // remmove
+        listener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, 
ref));
+
+        assertTrue(cache.getHolders().isEmpty());
+    }
+
     @Test public void testDuplicateRegistration() throws LoginException {
         final PathBasedHolderCache<AuthenticationRequirementHolder> cache = 
new PathBasedHolderCache<AuthenticationRequirementHolder>();
         final BundleContext context = mock(BundleContext.class);
@@ -304,4 +334,35 @@ public class SlingAuthenticatorServiceListenerTest {
                 new ServiceReference<?>[] {ref, ref, ref, ref, ref, ref},
                 new boolean[] {false, true, true, false, true, true});
     }
+
+    @Test public void testSwitchAllowDeny() throws LoginException {
+        final PathBasedHolderCache<AuthenticationRequirementHolder> cache = 
new PathBasedHolderCache<AuthenticationRequirementHolder>();
+        final BundleContext context = mock(BundleContext.class);
+        final ResourceMapper mapper = mock(ResourceMapper.class);
+        
when(mapper.getAllMappings("/path1")).thenReturn(Arrays.asList("/path1", 
"/path1a"));
+        
when(mapper.getAllMappings("/path2")).thenReturn(Arrays.asList("/path2", 
"/path2a"));
+
+        final SlingAuthenticatorServiceListener listener = 
SlingAuthenticatorServiceListener.createListener(context, callable -> 
callable.run(), createFactoryForMapper(mapper), cache);
+
+        // add
+        final ServiceReference<?> ref = createServiceReference(new String[] 
{"+/path1", "-/path2"});
+        listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, 
ref));
+
+        assertPaths(cache, new String[] {"/path1", "/path1a", "/path2", 
"/path2a"},
+                           new ServiceReference<?>[] {ref, ref, ref, ref},
+                           new boolean[] {true, true, false, false});
+
+        // update
+        when(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS)).thenReturn(new 
String[] {"-/path1", "/path2"});
+        listener.serviceChanged(new ServiceEvent(ServiceEvent.MODIFIED, ref));
+
+        assertPaths(cache, new String[] {"/path1", "/path1a", "/path2", 
"/path2a"},
+                new ServiceReference<?>[] {ref, ref, ref, ref},
+                new boolean[] {false, false, true, true});
+
+        // remmove
+        listener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, 
ref));
+
+        assertTrue(cache.getHolders().isEmpty());
+    }
 }

Reply via email to