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 60e6e22 SLING-9622 : Handle mapping within auth.core bundle and
update auth requirements when mapping changes
60e6e22 is described below
commit 60e6e221226db80a36923cb4c9a2b75910b9ea86
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Tue Aug 4 17:11:54 2020 +0200
SLING-9622 : Handle mapping within auth.core bundle and update auth
requirements when mapping changes
---
pom.xml | 2 +-
.../sling/auth/core/impl/PathBasedHolderCache.java | 6 +-
.../sling/auth/core/impl/SlingAuthenticator.java | 235 +++++++++-----------
.../impl/SlingAuthenticatorServiceListener.java | 237 +++++++++++++++++----
.../SlingAuthenticatorServiceListenerTest.java | 70 +++++-
5 files changed, 364 insertions(+), 186 deletions(-)
diff --git a/pom.xml b/pom.xml
index 5dd5b1b..f7d1a1a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.api</artifactId>
- <version>2.18.0</version>
+ <version>2.20.0</version>
<scope>provided</scope>
</dependency>
<dependency>
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 24dcf5a..ab0982d 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
@@ -49,11 +49,7 @@ public class PathBasedHolderCache<Type extends
PathBasedHolder> {
this.rwLock.writeLock().lock();
try {
- Map<String, SortedSet<Type>> byHostMap =
cache.get(holder.protocol);
- if (byHostMap == null) {
- byHostMap = new HashMap<String, SortedSet<Type>>();
- cache.put(holder.protocol, byHostMap);
- }
+ final Map<String, SortedSet<Type>> byHostMap =
cache.computeIfAbsent(holder.protocol, protocol -> new HashMap<>());
final SortedSet<Type> byPathSet = new TreeSet<Type>();
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 0e304a6..914099c 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
@@ -30,6 +30,7 @@ import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Executors;
import javax.jcr.SimpleCredentials;
import javax.security.auth.login.AccountLockedException;
@@ -47,10 +48,8 @@ import org.apache.sling.api.SlingConstants;
import org.apache.sling.api.auth.Authenticator;
import org.apache.sling.api.auth.NoAuthenticationHandlerException;
import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.auth.core.AuthConstants;
import org.apache.sling.auth.core.AuthUtil;
import org.apache.sling.auth.core.AuthenticationSupport;
@@ -258,11 +257,6 @@ public class SlingAuthenticator implements Authenticator,
*/
private static final String AUTH_INFO_PROP_FEEDBACK_HANDLER =
"$$sling.auth.AuthenticationFeedbackHandler$$";
- /**
- * Request attribute holding the resolved path
- */
- private static final String ATTR_RESOLVED_PATH =
SlingAuthenticator.class.getName().concat("/resolvedPath");
-
@Reference
private ResourceResolverFactory resourceResolverFactory;
@@ -339,15 +333,12 @@ public class SlingAuthenticator implements Authenticator,
@Reference(policy=ReferencePolicy.DYNAMIC, cardinality =
ReferenceCardinality.OPTIONAL)
private volatile EventAdmin eventAdmin;
- private ResourceResolver serviceResolver;
-
// ---------- SCR integration
@Activate
private void activate(final BundleContext bundleContext,
final Config config) throws LoginException {
modified(config);
- this.serviceResolver =
resourceResolverFactory.getServiceResourceResolver(null);
AuthenticatorWebConsolePlugin plugin = new
AuthenticatorWebConsolePlugin(
this);
@@ -364,7 +355,7 @@ public class SlingAuthenticator implements Authenticator,
Servlet.class, plugin, props);
serviceListener = SlingAuthenticatorServiceListener.createListener(
- bundleContext, this.authRequiredCache);
+ bundleContext, Executors.newSingleThreadExecutor(),
resourceResolverFactory, this.authRequiredCache);
authHandlerTracker = new AuthenticationHandlerTracker(bundleContext,
authHandlerCache);
@@ -458,7 +449,7 @@ public class SlingAuthenticator implements Authenticator,
}
if (serviceListener != null) {
- bundleContext.removeServiceListener(serviceListener);
+ serviceListener.stop(bundleContext);
serviceListener = null;
}
@@ -466,10 +457,6 @@ public class SlingAuthenticator implements Authenticator,
webConsolePlugin.unregister();
webConsolePlugin = null;
}
- if ( this.serviceResolver != null ) {
- this.serviceResolver.close();
- this.serviceResolver = null;
- }
}
// --------- AuthenticationSupport interface
@@ -492,34 +479,29 @@ public class SlingAuthenticator implements Authenticator,
@Override
public boolean handleSecurity(HttpServletRequest request,
HttpServletResponse response) {
+ // 0. Nothing to do, if the session is also in the request
+ // this might be the case if the request is handled as a result
+ // of a servlet container include inside another Sling request
+ Object sessionAttr = request.getAttribute(REQUEST_ATTRIBUTE_RESOLVER);
+ if (sessionAttr instanceof ResourceResolver) {
+ log.debug("handleSecurity: Request already authenticated, nothing
to do");
+ return true;
+ } else if (sessionAttr != null) {
+ // warn and remove existing non-session
+ log.warn("handleSecurity: Overwriting existing ResourceResolver
attribute ({})", sessionAttr);
+ request.removeAttribute(REQUEST_ATTRIBUTE_RESOLVER);
+ }
- try {
- // 0. Nothing to do, if the session is also in the request
- // this might be the case if the request is handled as a result
- // of a servlet container include inside another Sling request
- Object sessionAttr =
request.getAttribute(REQUEST_ATTRIBUTE_RESOLVER);
- if (sessionAttr instanceof ResourceResolver) {
- log.debug("handleSecurity: Request already authenticated,
nothing to do");
- return true;
- } else if (sessionAttr != null) {
- // warn and remove existing non-session
- log.warn("handleSecurity: Overwriting existing
ResourceResolver attribute ({})", sessionAttr);
- request.removeAttribute(REQUEST_ATTRIBUTE_RESOLVER);
- }
-
- boolean process = doHandleSecurity(request, response);
- if (process && expectAuthenticationHandler(request)) {
- log.warn("handleSecurity: AuthenticationHandler did not block
request; access denied");
- request.removeAttribute(AuthenticationHandler.FAILURE_REASON);
-
request.removeAttribute(AuthenticationHandler.FAILURE_REASON_CODE);
- AuthUtil.sendInvalid(request, response);
- return false;
- }
-
- return process;
- } finally {
- request.removeAttribute(ATTR_RESOLVED_PATH);
+ boolean process = doHandleSecurity(request, response);
+ if (process && expectAuthenticationHandler(request)) {
+ log.warn("handleSecurity: AuthenticationHandler did not block
request; access denied");
+ request.removeAttribute(AuthenticationHandler.FAILURE_REASON);
+ request.removeAttribute(AuthenticationHandler.FAILURE_REASON_CODE);
+ AuthUtil.sendInvalid(request, response);
+ return false;
}
+
+ return process;
}
private boolean doHandleSecurity(HttpServletRequest request,
HttpServletResponse response) {
@@ -597,54 +579,50 @@ public class SlingAuthenticator implements Authenticator,
throw new IllegalStateException("Response already committed");
}
- try {
- // select path used for authentication handler selection
- final Collection<AbstractAuthenticationHandlerHolder>[]
holdersArray = this.authHandlerCache
- .findApplicableHolders(request);
- final String path = getHandlerSelectionPath(request);
- boolean done = false;
- for (int m = 0; !done && m < holdersArray.length; m++) {
- final Collection<AbstractAuthenticationHandlerHolder>
holderList = holdersArray[m];
- if ( holderList != null ) {
- for (AbstractAuthenticationHandlerHolder holder :
holderList) {
- if (isNodeRequiresAuthHandler(path, holder.path)) {
- log.debug("login: requesting authentication using
handler: {}",
- holder);
-
- try {
- done = holder.requestCredentials(request,
response);
- } catch (IOException ioe) {
- log.error(
- "login: Failed sending authentication
request through handler "
- + holder + ", access forbidden", ioe);
- done = true;
- }
- if (done) {
- break;
- }
+ // select path used for authentication handler selection
+ final Collection<AbstractAuthenticationHandlerHolder>[] holdersArray =
this.authHandlerCache
+ .findApplicableHolders(request);
+ final String path = getHandlerSelectionPath(request);
+ boolean done = false;
+ for (int m = 0; !done && m < holdersArray.length; m++) {
+ final Collection<AbstractAuthenticationHandlerHolder> holderList =
holdersArray[m];
+ if ( holderList != null ) {
+ for (AbstractAuthenticationHandlerHolder holder : holderList) {
+ if (isNodeRequiresAuthHandler(path, holder.path)) {
+ log.debug("login: requesting authentication using
handler: {}",
+ holder);
+
+ try {
+ done = holder.requestCredentials(request,
response);
+ } catch (IOException ioe) {
+ log.error(
+ "login: Failed sending authentication request
through handler "
+ + holder + ", access forbidden", ioe);
+ done = true;
+ }
+ if (done) {
+ break;
}
}
}
}
+ }
- // fall back to HTTP Basic handler (if not done already)
- if (!done && httpBasicHandler != null) {
- done = httpBasicHandler.requestCredentials(request, response);
- }
+ // fall back to HTTP Basic handler (if not done already)
+ if (!done && httpBasicHandler != null) {
+ done = httpBasicHandler.requestCredentials(request, response);
+ }
- // no handler could send an authentication request, throw
- if (!done) {
- int size = 0;
- for (int m = 0; m < holdersArray.length; m++) {
- if (holdersArray[m] != null) {
- size += holdersArray[m].size();
- }
+ // no handler could send an authentication request, throw
+ if (!done) {
+ int size = 0;
+ for (int m = 0; m < holdersArray.length; m++) {
+ if (holdersArray[m] != null) {
+ size += holdersArray[m].size();
}
- log.info("login: No handler for request ({} handlers
available)", size);
- throw new NoAuthenticationHandlerException();
}
- } finally {
- request.removeAttribute(ATTR_RESOLVED_PATH);
+ log.info("login: No handler for request ({} handlers available)",
size);
+ throw new NoAuthenticationHandlerException();
}
}
@@ -664,31 +642,27 @@ public class SlingAuthenticator implements Authenticator,
// make sure impersonation is dropped
setSudoCookie(request, response, new AuthenticationInfo("dummy",
request.getRemoteUser()));
- try {
- final String path = getHandlerSelectionPath(request);
- final Collection<AbstractAuthenticationHandlerHolder>[]
holdersArray = this.authHandlerCache
- .findApplicableHolders(request);
- for (int m = 0; m < holdersArray.length; m++) {
- final Collection<AbstractAuthenticationHandlerHolder>
holderSet = holdersArray[m];
- if (holderSet != null) {
- for (AbstractAuthenticationHandlerHolder holder :
holderSet) {
- if (isNodeRequiresAuthHandler(path, holder.path)) {
- log.debug("logout: dropping authentication using
handler: {}",
- holder);
-
- try {
- holder.dropCredentials(request, response);
- } catch (IOException ioe) {
- log.error(
- "logout: Failed dropping authentication
through handler "
- + holder, ioe);
- }
+ final String path = getHandlerSelectionPath(request);
+ final Collection<AbstractAuthenticationHandlerHolder>[] holdersArray =
this.authHandlerCache
+ .findApplicableHolders(request);
+ for (int m = 0; m < holdersArray.length; m++) {
+ final Collection<AbstractAuthenticationHandlerHolder> holderSet =
holdersArray[m];
+ if (holderSet != null) {
+ for (AbstractAuthenticationHandlerHolder holder : holderSet) {
+ if (isNodeRequiresAuthHandler(path, holder.path)) {
+ log.debug("logout: dropping authentication using
handler: {}",
+ holder);
+
+ try {
+ holder.dropCredentials(request, response);
+ } catch (IOException ioe) {
+ log.error(
+ "logout: Failed dropping authentication
through handler "
+ + holder, ioe);
}
}
}
}
- } finally {
- request.removeAttribute(ATTR_RESOLVED_PATH);
}
if (httpBasicHandler != null) {
@@ -767,32 +741,27 @@ public class SlingAuthenticator implements Authenticator,
// ---------- internal
- private String getPath(HttpServletRequest request) {
- String path = (String)request.getAttribute(ATTR_RESOLVED_PATH);
- if ( path == null ) {
- final StringBuilder sb = new StringBuilder();
- if (request.getServletPath() != null) {
- sb.append(request.getServletPath());
- }
- if (request.getPathInfo() != null) {
- sb.append(request.getPathInfo());
- }
- path = sb.toString();
- // Get the path used to select the authenticator, if the
SlingServlet
- // itself has been requested without any more info, this will be
empty
- // and we assume the root (SLING-722)
- if (path.length() == 0) {
- path = "/";
- }
-
- if ( !path.equals("/") && serviceResolver != null ) { // NULL
check to let unit tests pass
- final Resource mappedResource = serviceResolver.resolve(path);
- if ( !ResourceUtil.isNonExistingResource(mappedResource) ) {
- path = mappedResource.getPath();
- }
- }
- request.setAttribute(ATTR_RESOLVED_PATH, path);
+ /**
+ * Get the request path from the request
+ * @param request The request
+ * @return The path
+ */
+ private String getPath(final HttpServletRequest request) {
+ final StringBuilder sb = new StringBuilder();
+ if (request.getServletPath() != null) {
+ sb.append(request.getServletPath());
}
+ if (request.getPathInfo() != null) {
+ sb.append(request.getPathInfo());
+ }
+ String path = sb.toString();
+ // Get the path used to select the authenticator, if the SlingServlet
+ // itself has been requested without any more info, this will be empty
+ // and we assume the root (SLING-722)
+ if (path.length() == 0) {
+ path = "/";
+ }
+
return path;
}
@@ -1010,10 +979,6 @@ public class SlingAuthenticator implements Authenticator,
}
private boolean isNodeRequiresAuthHandler(String path, String holderPath) {
- if (path == null || holderPath == null) {
- return false;
- }
-
if (("/").equals(holderPath)) {
return true;
}
@@ -1028,10 +993,8 @@ public class SlingAuthenticator implements Authenticator,
return true;
}
- if (path.startsWith(holderPath)) {
- if (path.charAt(holderPathLength) == '/' ||
path.charAt(holderPathLength) == '.') {
- return true;
- }
+ if (path.startsWith(holderPath) && (path.charAt(holderPathLength) ==
'/' || path.charAt(holderPathLength) == '.')) {
+ return true;
}
return false;
}
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 45a0bba..d991580 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
@@ -19,11 +19,22 @@
package org.apache.sling.auth.core.impl;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Dictionary;
import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.sling.api.SlingConstants;
+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;
@@ -32,62 +43,195 @@ import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
-public class SlingAuthenticatorServiceListener implements AllServiceListener {
+/**
+ * Service listener keeping track of all auth requirements registered in
+ * the service registry.
+ *
+ */
+public class SlingAuthenticatorServiceListener implements AllServiceListener,
EventHandler {
+
+ private static String FILTER_EXPR =
"(".concat(AuthConstants.AUTH_REQUIREMENTS).concat("=*)");
+
+ private static final Long UPDATE = 0L;
+
+ private static final Long CLEAR = -1L;
+
+ private final ResourceResolverFactory resolverFactory;
private final PathBasedHolderCache<AuthenticationRequirementHolder>
authRequiredCache;
- private final HashMap<Object, Set<String>> regProps = new HashMap<Object,
Set<String>>();
+ private final Map<Long, Set<String>> regProps = new ConcurrentHashMap<>();
+
+ private final Map<Long, List<AuthenticationRequirementHolder>> props = new
ConcurrentHashMap<>();
+
+ private ServiceRegistration<EventHandler> serviceRegistration;
- private final HashMap<Object, List<AuthenticationRequirementHolder>> props
= new HashMap<Object, List<AuthenticationRequirementHolder>>();
+ private final Map<Long, Action> processingQueue = new LinkedHashMap<>();
+
+ private final Executor executor;
+
+ private final AtomicBoolean backgroundJobRunning = new
AtomicBoolean(false);
static SlingAuthenticatorServiceListener createListener(
final BundleContext context,
+ final Executor executor,
+ final ResourceResolverFactory factory,
final PathBasedHolderCache<AuthenticationRequirementHolder>
authRequiredCache) {
- SlingAuthenticatorServiceListener listener = new
SlingAuthenticatorServiceListener(authRequiredCache);
+
+ final SlingAuthenticatorServiceListener listener = new
SlingAuthenticatorServiceListener(authRequiredCache,
+ executor,
+ factory);
try {
- final String filter = "(" + AuthConstants.AUTH_REQUIREMENTS +
"=*)";
- context.addServiceListener(listener, filter);
- ServiceReference<?>[] refs = context.getAllServiceReferences(null,
- filter);
+ context.addServiceListener(listener, FILTER_EXPR);
+ final Dictionary<String, Object> props = new Hashtable<>();
+ props.put(EventConstants.EVENT_TOPIC,
SlingConstants.TOPIC_RESOURCE_RESOLVER_MAPPING_CHANGED);
+
listener.setServiceRegistration(context.registerService(EventHandler.class,
listener, props));
+ ServiceReference<?>[] refs = context.getAllServiceReferences(null,
FILTER_EXPR);
if (refs != null) {
- for (ServiceReference<?> ref : refs) {
- listener.addService(ref);
+ for (final ServiceReference<?> ref : refs) {
+ final Long id =
(Long)ref.getProperty(Constants.SERVICE_ID);
+ listener.queue(id, new Action(ActionType.ADDED, ref));
}
}
+
+ listener.schedule();
+
return listener;
} catch (InvalidSyntaxException ise) {
}
return null;
}
- private SlingAuthenticatorServiceListener(final
PathBasedHolderCache<AuthenticationRequirementHolder> authRequiredCache) {
+ private SlingAuthenticatorServiceListener(final
PathBasedHolderCache<AuthenticationRequirementHolder> authRequiredCache,
+ final Executor executor,
+ final ResourceResolverFactory factory) {
this.authRequiredCache = authRequiredCache;
+ this.executor = executor;
+ this.resolverFactory = factory;
+ }
+
+ void setServiceRegistration(final ServiceRegistration<EventHandler> reg) {
+ this.serviceRegistration = reg;
+ }
+
+ public void stop(final BundleContext bundleContext) {
+ bundleContext.removeServiceListener(this);
+ if ( this.serviceRegistration != null ) {
+ this.serviceRegistration.unregister();
+ this.serviceRegistration = null;
+ }
+ queue(CLEAR, null);
+ backgroundJobRunning.set(false);
+ }
+
+ private void schedule() {
+ if ( this.backgroundJobRunning.compareAndSet(false, true) ) {
+ this.executor.execute(() -> { processQueue(); });
+ }
}
@Override
public void serviceChanged(final ServiceEvent event) {
- synchronized ( props ) {
- // modification of service properties, unregistration of the
- // service or service properties does not contain requirements
- // property any longer (new event with type 8 added in OSGi Core
- // 4.2)
- if ((event.getType() & (ServiceEvent.UNREGISTERING |
ServiceEvent.MODIFIED_ENDMATCH)) != 0) {
- removeService(event.getServiceReference());
+ // modification of service properties, unregistration of the
+ // service or service properties does not contain requirements
+ // property any longer (new event with type 8 added in OSGi Core
+ // 4.2)
+ final Long id =
(Long)event.getServiceReference().getProperty(Constants.SERVICE_ID);
+ if ((event.getType() & (ServiceEvent.UNREGISTERING |
ServiceEvent.MODIFIED_ENDMATCH)) != 0) {
+ queue(id, new Action(ActionType.REMOVED,
event.getServiceReference()));
+ }
+
+ if ((event.getType() & ServiceEvent.MODIFIED ) != 0) {
+ queue(id, new Action(ActionType.MODIFIED,
event.getServiceReference()));
+ }
+
+ // add requirements for newly registered services and for
+ // updated services
+ if ((event.getType() & ServiceEvent.REGISTERED ) != 0) {
+ queue(id, new Action(ActionType.ADDED,
event.getServiceReference()));
+ }
+ schedule();
+ }
+
+ @Override
+ public void handleEvent(final Event event) {
+ queue(UPDATE, null);
+ schedule();
+ }
+
+ /**
+ * Queue a new action
+ * @param id The id of the service
+ * @param action The action to take
+ */
+ private void queue(final Long id, final Action action) {
+ synchronized ( this.processingQueue ) {
+ if ( id == CLEAR ) {
+ this.processingQueue.clear();
+ } else if ( id == UPDATE ) {
+ for(final Long updateId: this.props.keySet()) {
+ this.processingQueue.putIfAbsent(updateId, new
Action(ActionType.UPDATE, null));
+ }
+ } else {
+ this.processingQueue.remove(id);
+ this.processingQueue.put(id, action);
}
+ }
+ }
- if ((event.getType() & ServiceEvent.MODIFIED ) != 0) {
- modifiedService(event.getServiceReference());
+ private void processQueue() {
+ ResourceResolver resolver = null;
+ ResourceMapper mapper = null;
+ try {
+ while ( this.backgroundJobRunning.get() ) {
+ Action action = null;
+ synchronized ( this.processingQueue ) {
+ final Iterator<Action> iter =
this.processingQueue.values().iterator();
+ if ( iter.hasNext() ) {
+ action = iter.next();
+ iter.remove();
+ }
+ }
+ if ( action == null ) {
+ synchronized ( this.processingQueue ) {
+ this.backgroundJobRunning.compareAndSet(true,
!this.processingQueue.isEmpty());
+ }
+ } else {
+ if ( action.type != ActionType.REMOVED ) {
+ if ( mapper == null ) {
+ try {
+ resolver =
this.resolverFactory.getServiceResourceResolver(null);
+ mapper =
resolver.adaptTo(ResourceMapper.class);
+ } catch ( final
org.apache.sling.api.resource.LoginException le) {
+ // ignore
+ }
+ }
+ }
+ process(mapper, action);
+ }
}
- // add requirements for newly registered services and for
- // updated services
- if ((event.getType() & ServiceEvent.REGISTERED ) != 0) {
- addService(event.getServiceReference());
+ } finally {
+ if ( resolver != null ) {
+ resolver.close();
}
}
}
+ private void process(final ResourceMapper mapper, final Action action) {
+ switch ( action.type ) {
+ case ADDED : this.addService(mapper, action.reference); break;
+ case REMOVED : this.removeService(action.reference); break;
+ case MODIFIED : this.modifiedService(mapper, action.reference);
break;
+ case UPDATE: // TODO
+ }
+ }
+
/**
* Register all known services.
*/
@@ -107,11 +251,17 @@ public class SlingAuthenticatorServiceListener implements
AllServiceListener {
}
}
- private Set<String> buildPathsSet(final String[] authReqPaths) {
+ private Set<String> buildPathsSet(final ResourceMapper mapper, final
String[] authReqPaths) {
final Set<String> paths = new HashSet<>();
for(final String authReq : authReqPaths) {
if (authReq != null && authReq.length() > 0) {
paths.add(authReq);
+
+ if ( mapper != null ) {
+ for(final String mappedPath :
mapper.getAllMappings(authReq)) {
+ paths.add(mappedPath);
+ }
+ }
}
}
return paths;
@@ -121,22 +271,21 @@ public class SlingAuthenticatorServiceListener implements
AllServiceListener {
* Process a new service with auth requirements
* @param ref The service reference
*/
- private void addService(final ServiceReference<?> ref) {
+ private void addService(final ResourceMapper mapper, final
ServiceReference<?> ref) {
final String[] authReqPaths =
PropertiesUtil.toStringArray(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS));
if ( authReqPaths != null ) {
- final Set<String> paths = buildPathsSet(authReqPaths);
+ final Set<String> paths = buildPathsSet(mapper, authReqPaths);
if ( !paths.isEmpty() ) {
final List<AuthenticationRequirementHolder> authReqList = new
ArrayList<AuthenticationRequirementHolder>();
for (final String authReq : paths) {
- authReqList.add(AuthenticationRequirementHolder.fromConfig(
- authReq, ref));
+
authReqList.add(AuthenticationRequirementHolder.fromConfig(authReq, ref));
}
// keep original set for modified
- regProps.put(ref.getProperty(Constants.SERVICE_ID), paths);
+ regProps.put((Long)ref.getProperty(Constants.SERVICE_ID),
paths);
registerService(authReqList);
- props.put(ref.getProperty(Constants.SERVICE_ID), authReqList);
+ props.put((Long)ref.getProperty(Constants.SERVICE_ID),
authReqList);
}
}
}
@@ -145,14 +294,14 @@ public class SlingAuthenticatorServiceListener implements
AllServiceListener {
* Process a modified service with auth requirements
* @param ref The service reference
*/
- private void modifiedService(final ServiceReference<?> ref) {
+ private void modifiedService(final ResourceMapper mapper, final
ServiceReference<?> ref) {
final String[] authReqPaths =
PropertiesUtil.toStringArray(ref.getProperty(AuthConstants.AUTH_REQUIREMENTS));
if ( authReqPaths != null ) {
final Set<String> oldPaths =
regProps.get(ref.getProperty(Constants.SERVICE_ID));
if ( oldPaths == null ) {
- addService(ref);
+ addService(mapper, ref);
} else {
- final Set<String> paths = buildPathsSet(authReqPaths);
+ final Set<String> paths = buildPathsSet(mapper, authReqPaths);
if ( paths.isEmpty() ) {
removeService(ref);
} else {
@@ -174,7 +323,7 @@ public class SlingAuthenticatorServiceListener implements
AllServiceListener {
authRequiredCache.addHolder(holder);
}
}
- regProps.put(ref.getProperty(Constants.SERVICE_ID), paths);
+ regProps.put((Long)ref.getProperty(Constants.SERVICE_ID),
paths);
}
}
} else {
@@ -195,4 +344,20 @@ public class SlingAuthenticatorServiceListener implements
AllServiceListener {
}
regProps.remove(ref.getProperty(Constants.SERVICE_ID));
}
+
+ public enum ActionType {
+ ADDED, MODIFIED, REMOVED, UPDATE
+ }
+
+ public static final class Action {
+
+ public final ActionType type;
+
+ public final ServiceReference<?> reference;
+
+ public Action(final ActionType type, final ServiceReference<?> ref) {
+ this.type = type;
+ this.reference = ref;
+ }
+ }
}
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 c8d36dc..a5724c3 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
@@ -25,8 +25,13 @@ import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import org.apache.sling.api.resource.LoginException;
+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.junit.Test;
import org.osgi.framework.BundleContext;
@@ -75,10 +80,24 @@ public class SlingAuthenticatorServiceListenerTest {
return ref;
}
- @Test public void testAddRemoveRegistration() {
+ private ResourceResolverFactory createFactoryForMapper(final
ResourceMapper mapper) throws LoginException {
+ final ResourceResolverFactory factory =
mock(ResourceResolverFactory.class);
+
+ final ResourceResolver resolver = mock(ResourceResolver.class);
+
+ when(factory.getServiceResourceResolver(null)).thenReturn(resolver);
+
+ when(resolver.adaptTo(ResourceMapper.class)).thenReturn(mapper);
+
+ return factory;
+ }
+
+ @Test public void testAddRemoveRegistration() throws LoginException {
final PathBasedHolderCache<AuthenticationRequirementHolder> cache =
new PathBasedHolderCache<AuthenticationRequirementHolder>();
final BundleContext context = mock(BundleContext.class);
- final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, cache);
+ final ResourceMapper mapper = mock(ResourceMapper.class);
+
when(mapper.getAllMappings("/path1")).thenReturn(Collections.singleton("/path1"));
+ final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, callable ->
callable.run(), createFactoryForMapper(mapper), cache);
assertTrue(cache.getHolders().isEmpty());
@@ -95,10 +114,14 @@ public class SlingAuthenticatorServiceListenerTest {
assertTrue(cache.getHolders().isEmpty());
}
- @Test public void testDuplicateRegistration() {
+ @Test public void testDuplicateRegistration() throws LoginException {
final PathBasedHolderCache<AuthenticationRequirementHolder> cache =
new PathBasedHolderCache<AuthenticationRequirementHolder>();
final BundleContext context = mock(BundleContext.class);
- final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, cache);
+ final ResourceMapper mapper = mock(ResourceMapper.class);
+
when(mapper.getAllMappings("/path1")).thenReturn(Collections.singleton("/path1"));
+
when(mapper.getAllMappings("/path2")).thenReturn(Collections.singleton("/path2"));
+
when(mapper.getAllMappings("/path3")).thenReturn(Collections.singleton("/path3"));
+ final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, callable ->
callable.run(), createFactoryForMapper(mapper), cache);
final ServiceReference<?> ref1 = createServiceReference(new String[]
{"/path1", "/path1", "/path2"});
listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED,
ref1));
@@ -117,10 +140,16 @@ public class SlingAuthenticatorServiceListenerTest {
assertTrue(cache.getHolders().isEmpty());
}
- @Test public void testAddRemoveRegistrations() {
+ @Test public void testAddRemoveRegistrations() throws LoginException {
final PathBasedHolderCache<AuthenticationRequirementHolder> cache =
new PathBasedHolderCache<AuthenticationRequirementHolder>();
final BundleContext context = mock(BundleContext.class);
- final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, cache);
+ final ResourceMapper mapper = mock(ResourceMapper.class);
+
when(mapper.getAllMappings("/path1")).thenReturn(Collections.singleton("/path1"));
+
when(mapper.getAllMappings("/path2")).thenReturn(Collections.singleton("/path2"));
+
when(mapper.getAllMappings("/path3")).thenReturn(Collections.singleton("/path3"));
+
when(mapper.getAllMappings("/path4")).thenReturn(Collections.singleton("/path4"));
+
when(mapper.getAllMappings("/path5")).thenReturn(Collections.singleton("/path5"));
+ final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, callable ->
callable.run(), createFactoryForMapper(mapper), cache);
final ServiceReference<?> ref1 = createServiceReference(new String[]
{"/path1"});
listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED,
ref1));
@@ -147,10 +176,16 @@ public class SlingAuthenticatorServiceListenerTest {
assertTrue(cache.getHolders().isEmpty());
}
- @Test public void testModifyRegistration() {
+ @Test public void testModifyRegistration() throws LoginException {
final PathBasedHolderCache<AuthenticationRequirementHolder> cache =
new PathBasedHolderCache<AuthenticationRequirementHolder>();
final BundleContext context = mock(BundleContext.class);
- final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, cache);
+ final ResourceMapper mapper = mock(ResourceMapper.class);
+
when(mapper.getAllMappings("/path1")).thenReturn(Collections.singleton("/path1"));
+
when(mapper.getAllMappings("/path2")).thenReturn(Collections.singleton("/path2"));
+
when(mapper.getAllMappings("/path3")).thenReturn(Collections.singleton("/path3"));
+
when(mapper.getAllMappings("/path4")).thenReturn(Collections.singleton("/path4"));
+
when(mapper.getAllMappings("/path5")).thenReturn(Collections.singleton("/path5"));
+ final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, callable ->
callable.run(), createFactoryForMapper(mapper), cache);
final ServiceReference<?> ref1 = createServiceReference(new String[]
{"/path1", "/path2", "/path3"});
listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED,
ref1));
@@ -169,4 +204,23 @@ public class SlingAuthenticatorServiceListenerTest {
assertTrue(cache.getHolders().isEmpty());
}
+
+ @Test public void testRegistrationWithMapping() 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",
"/path2", "/path3"));
+ final SlingAuthenticatorServiceListener listener =
SlingAuthenticatorServiceListener.createListener(context, callable ->
callable.run(), createFactoryForMapper(mapper), cache);
+
+ final ServiceReference<?> ref = createServiceReference(new String[]
{"/path1"});
+ listener.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED,
ref));
+
+ assertPaths(cache, new String[] {"/path1", "/path2", "/path3"},
+ new ServiceReference<?>[] {ref, ref, ref});
+
+ listener.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING,
ref));
+
+ assertTrue(cache.getHolders().isEmpty());
+ }
+
}