SLING-6419: Switch JcrSystemUserValidator to a service user preventing endless recursion using a thread local.
diff --git a/src/main/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidator.java b/src/main/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidator.java
index 4101e44..5080140 100644
--- a/src/main/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidator.java
+++ b/src/main/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidator.java
@@ -82,6 +82,16 @@
private boolean allowOnlySystemUsers;
+ /*
+ * We have to prevent a cycle if we are trying to login ourselves
+ */
+ private final ThreadLocal<Boolean> cycleDetection = new ThreadLocal<Boolean>() {
+ @Override
+ protected Boolean initialValue() {
+ return false;
+ }
+ };
+
public JcrSystemUserValidator() {
Method m = null;
try {
@@ -99,6 +109,9 @@
@Override
public boolean isValid(final String serviceUserId, final String serviceName, final String subServiceName) {
+ if (cycleDetection.get()) {
+ return true;
+ }
if (serviceUserId == null) {
log.debug("The provided service user id is null");
return false;
@@ -111,22 +124,20 @@
log.debug("The provided service user id '{}' has been already validated and is a known JCR system user id", serviceUserId);
return true;
} else {
- Session administrativeSession = null;
+ Session session = null;
try {
try {
/*
- * TODO: Instead of using the deprecated loginAdministrative
- * method, this bundle could be configured with an appropriate
- * user for service authentication and do:
- * tmpSession = repository.loginService(null, workspace);
- * For now, we keep loginAdministrative as switching to a service user
- * will result in a endless recursion (this method checks if
- * a service user is allowed, so using a service user here
- * calls this method again...and again...and again)
+ * We have to prevent a cycle if we are trying to login ourselves
*/
- administrativeSession = repository.loginAdministrative(null);
- if (administrativeSession instanceof JackrabbitSession) {
- final UserManager userManager = ((JackrabbitSession) administrativeSession).getUserManager();
+ cycleDetection.set(true);
+ try {
+ session = repository.loginService(null, null);
+ } finally {
+ cycleDetection.set(false);
+ }
+ if (session instanceof JackrabbitSession) {
+ final UserManager userManager = ((JackrabbitSession) session).getUserManager();
final Authorizable authorizable = userManager.getAuthorizable(serviceUserId);
if (isValidSystemUser(authorizable)) {
validIds.add(serviceUserId);
@@ -138,8 +149,8 @@
log.warn("Could not get user information", e);
}
} finally {
- if (administrativeSession != null) {
- administrativeSession.logout();
+ if (session != null) {
+ session.logout();
}
}
log.warn("The provided service user id '{}' is not a known JCR system user id and therefore not allowed in the Sling Service User Mapper.", serviceUserId);
@@ -149,6 +160,9 @@
@Override
public boolean isValid(Iterable<String> servicePrincipalNames, String serviceName, String subServiceName) {
+ if (cycleDetection.get()) {
+ return true;
+ }
if (servicePrincipalNames == null) {
log.debug("The provided service principal names are null");
return false;
@@ -157,7 +171,8 @@
log.debug("There is no enforcement of JCR system users, therefore service principal names '{}' are valid", servicePrincipalNames);
return true;
}
- Session administrativeSession = null;
+
+ Session session = null;
UserManager userManager = null;
Set<String> invalid = new HashSet<>();
try {
@@ -165,20 +180,18 @@
if (validPrincipalNames.contains(pName)) {
log.debug("The provided service principal name '{}' has been already validated and is a known JCR system user", pName);
} else {
- /*
- * TODO: Instead of using the deprecated loginAdministrative
- * method, this bundle could be configured with an appropriate
- * user for service authentication and do:
- * tmpSession = repository.loginService(null, workspace);
- * For now, we keep loginAdministrative as switching to a service user
- * will result in a endless recursion (this method checks if
- * a sservice user is allowed, so using a service user here
- * calls this method again...and again...and again)
- */
- if (administrativeSession == null) {
- administrativeSession = repository.loginAdministrative(null);
- if (administrativeSession instanceof JackrabbitSession) {
- userManager = ((JackrabbitSession) administrativeSession).getUserManager();
+ if (session == null) {
+ /*
+ * We have to prevent a cycle if we are trying to login ourselves
+ */
+ cycleDetection.set(true);
+ try {
+ session = repository.loginService(null, null);
+ } finally {
+ cycleDetection.set(false);
+ }
+ if (session instanceof JackrabbitSession) {
+ userManager = ((JackrabbitSession) session).getUserManager();
} else {
log.debug("Unable to validate service user principals, JackrabbitSession expected.");
return false;
@@ -203,8 +216,8 @@
} catch (final RepositoryException e) {
log.warn("Could not get user information", e);
} finally {
- if (administrativeSession != null) {
- administrativeSession.logout();
+ if (session != null) {
+ session.logout();
}
}
return invalid.isEmpty();
diff --git a/src/test/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidatorTest.java b/src/test/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidatorTest.java
index 929d431..ae36024 100644
--- a/src/test/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidatorTest.java
+++ b/src/test/java/org/apache/sling/jcr/resource/internal/JcrSystemUserValidatorTest.java
@@ -19,10 +19,16 @@
import java.lang.reflect.Field;
import java.util.Collections;
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
import javax.naming.NamingException;
import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
+import org.apache.sling.jcr.api.SlingRepository;
import org.junit.Before;
import org.junit.Test;
@@ -38,7 +44,75 @@
jcrSystemUserValidator = new JcrSystemUserValidator();
Field repositoryField = jcrSystemUserValidator.getClass().getDeclaredField("repository");
repositoryField.setAccessible(true);
- repositoryField.set(jcrSystemUserValidator, getRepository());
+ final SlingRepository delegate = getRepository();
+
+ SlingRepository repository = new SlingRepository() {
+ @Override
+ public String getDefaultWorkspace() {
+ return delegate.getDefaultWorkspace();
+ }
+
+ @Override
+ public Session loginAdministrative(String s) throws LoginException, RepositoryException {
+ return delegate.loginAdministrative(s);
+ }
+
+ @Override
+ public Session loginService(String s, String s1) throws LoginException, RepositoryException {
+ return delegate.loginAdministrative(s1);
+ }
+
+ @Override
+ public String[] getDescriptorKeys() {
+ return delegate.getDescriptorKeys();
+ }
+
+ @Override
+ public boolean isStandardDescriptor(String s) {
+ return delegate.isStandardDescriptor(s);
+ }
+
+ @Override
+ public boolean isSingleValueDescriptor(String s) {
+ return delegate.isSingleValueDescriptor(s);
+ }
+
+ @Override
+ public Value getDescriptorValue(String s) {
+ return delegate.getDescriptorValue(s);
+ }
+
+ @Override
+ public Value[] getDescriptorValues(String s) {
+ return delegate.getDescriptorValues(s);
+ }
+
+ @Override
+ public String getDescriptor(String s) {
+ return delegate.getDescriptor(s);
+ }
+
+ @Override
+ public Session login(Credentials credentials, String s) throws LoginException, NoSuchWorkspaceException, RepositoryException {
+ return delegate.login(credentials, s);
+ }
+
+ @Override
+ public Session login(Credentials credentials) throws LoginException, RepositoryException {
+ return delegate.login(credentials);
+ }
+
+ @Override
+ public Session login(String s) throws LoginException, NoSuchWorkspaceException, RepositoryException {
+ return delegate.login(s);
+ }
+
+ @Override
+ public Session login() throws LoginException, RepositoryException {
+ return delegate.login();
+ }
+ };
+ repositoryField.set(jcrSystemUserValidator, repository);
}
@Test