From 121341a51a80d0ef47f536e075e0bfad0aa715e0 Mon Sep 17 00:00:00 2001
From: rjung
Date: Fri, 19 Sep 2008 21:23:22 +0000
Subject: [PATCH] Improve Active Directory compatibility of the JNDIRealm.
AD often returns referrals and when iterating through
NamingEnumerations those produce PartialResultsException
we need to ignore. Since there is no robust way of detecting
whether they are actually thrown because of AD referrals,
we keep the handling configurable.
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@697248 13f79535-47bb-0310-9956-ffa450edef68
---
java/org/apache/catalina/realm/JNDIRealm.java | 100 +++++++++++++++++++++-----
webapps/docs/config/realm.xml | 44 ++++++++----
webapps/docs/realm-howto.xml | 19 +++++
3 files changed, 133 insertions(+), 30 deletions(-)
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
index 150738b20..f28c03fb4 100644
--- a/java/org/apache/catalina/realm/JNDIRealm.java
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
@@ -23,6 +23,7 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import javax.naming.Context;
@@ -35,6 +36,7 @@ import javax.naming.NamingException;
import javax.naming.NameParser;
import javax.naming.Name;
import javax.naming.AuthenticationException;
+import javax.naming.PartialResultException;
import javax.naming.ServiceUnavailableException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
@@ -228,9 +230,20 @@ public class JNDIRealm extends RealmBase {
/**
- * How should we handle referrals? Microsoft Active Directory can't handle
- * the default case, so an application authenticating against AD must
- * set referrals to "follow".
+ * Should we ignore PartialResultExceptions when iterating over NamingEnumerations?
+ * Microsoft Active Directory often returns referrals, which lead
+ * to PartialResultExceptions. Unfortunately there's no stable way to detect,
+ * if the Exceptions really come from an AD referral.
+ * Set to true to ignore PartialResultExceptions.
+ */
+ protected boolean adCompat = false;
+
+
+ /**
+ * How should we handle referrals? Microsoft Active Directory often returns
+ * referrals. If you need to follow them set referrals to "follow".
+ * Caution: if your DNS is not part of AD, the LDAP client lib might try
+ * to resolve your domain name in DNS to find another LDAP server.
*/
protected String referrals = null;
@@ -500,6 +513,23 @@ public class JNDIRealm extends RealmBase {
/**
+ * Returns the current settings for handling PartialResultExceptions
+ */
+ public boolean getAdCompat () {
+ return adCompat;
+ }
+
+
+ /**
+ * How do we handle PartialResultExceptions?
+ * True: ignore all PartialResultExceptions.
+ */
+ public void setAdCompat (boolean adCompat) {
+ this.adCompat = adCompat;
+ }
+
+
+ /**
* Returns the current settings for handling JNDI referrals.
*/
public String getReferrals () {
@@ -948,6 +978,12 @@ public class JNDIRealm extends RealmBase {
if (checkCredentials(context, user, credentials)) {
// Search for additional roles
List roles = getRoles(context, user);
+ if (containerLog.isDebugEnabled()) {
+ Iterator it = roles.iterator();
+ while (it.hasNext()) {
+ containerLog.debug("Found role: " + it.next());
+ }
+ }
return (new GenericPrincipal(this,
username,
credentials,
@@ -976,6 +1012,12 @@ public class JNDIRealm extends RealmBase {
// Search for additional roles
List roles = getRoles(context, user);
+ if (containerLog.isDebugEnabled()) {
+ Iterator it = roles.iterator();
+ while (it.hasNext()) {
+ containerLog.debug("Found role: " + it.next());
+ }
+ }
// Create and return a suitable Principal for this user
return (new GenericPrincipal(this, username, credentials, roles));
@@ -1114,18 +1156,30 @@ public class JNDIRealm extends RealmBase {
// Fail if no entries found
- if (results == null || !results.hasMore()) {
- return (null);
+ try {
+ if (results == null || !results.hasMore()) {
+ return (null);
+ }
+ } catch (PartialResultException ex) {
+ if (!adCompat)
+ throw ex;
+ else
+ return (null);
}
// Get result for the first entry found
SearchResult result = (SearchResult)results.next();
// Check no further entries were found
- if (results.hasMore()) {
- if(containerLog.isInfoEnabled())
- containerLog.info("username " + username + " has multiple entries");
- return (null);
+ try {
+ if (results.hasMore()) {
+ if(containerLog.isInfoEnabled())
+ containerLog.info("username " + username + " has multiple entries");
+ return (null);
+ }
+ } catch (PartialResultException ex) {
+ if (!adCompat)
+ throw ex;
}
// Get the entry's distinguished name
@@ -1412,12 +1466,17 @@ public class JNDIRealm extends RealmBase {
context.search(roleBase, filter, controls);
if (results == null)
return (list); // Should never happen, but just in case ...
- while (results.hasMore()) {
- SearchResult result = (SearchResult) results.next();
- Attributes attrs = result.getAttributes();
- if (attrs == null)
- continue;
- list = addAttributeValues(roleName, attrs, list);
+ try {
+ while (results.hasMore()) {
+ SearchResult result = (SearchResult) results.next();
+ Attributes attrs = result.getAttributes();
+ if (attrs == null)
+ continue;
+ list = addAttributeValues(roleName, attrs, list);
+ }
+ } catch (PartialResultException ex) {
+ if (!adCompat)
+ throw ex;
}
@@ -1493,9 +1552,14 @@ public class JNDIRealm extends RealmBase {
if (attr == null)
return (values);
NamingEnumeration e = attr.getAll();
- while(e.hasMore()) {
- String value = (String)e.next();
- values.add(value);
+ try {
+ while(e.hasMore()) {
+ String value = (String)e.next();
+ values.add(value);
+ }
+ } catch (PartialResultException ex) {
+ if (!adCompat)
+ throw ex;
}
return values;
}
diff --git a/webapps/docs/config/realm.xml b/webapps/docs/config/realm.xml
index ac1e313f7..58407a11d 100644
--- a/webapps/docs/config/realm.xml
+++ b/webapps/docs/config/realm.xml
@@ -292,17 +292,27 @@
information from the directory:
-
- If a socket connection can not be made to the provider at
- the connectionURL an attempt will be made to use the
- alternateURL.
-
-
-
- A string specifying the type of authentication to use.
- "none", "simple", "strong" or a provider specific definition
- can be used. If no value is given the providers default is used.
-
+
+
+ Microsoft Active Directory often returns referrals.
+ When iterating over NamingEnumerations these lead to
+ PartialResultExceptions. If you want us to ignore those exceptions,
+ set this attribute to "true". Unfortunately there's no stable way
+ to detect, if the Exceptions really come from an AD referral.
+ The default value is "false".
+
+
+
+ If a socket connection can not be made to the provider at
+ the connectionURL an attempt will be made to use the
+ alternateURL.
+
+
+
+ A string specifying the type of authentication to use.
+ "none", "simple", "strong" or a provider specific definition
+ can be used. If no value is given the providers default is used.
+
A role name assigned to each successfully authenticated user in
@@ -336,7 +346,7 @@
to acquire our JNDI InitialContext. By default,
assumes that the standard JNDI LDAP provider will be utilized.
-
+
A string specifying how aliases are to be dereferenced during
search operations. The allowed values are "always", "never",
@@ -357,6 +367,16 @@
the providers default is used.
+
+ How do we handle JNDI referrals? Allowed values are
+ "ignore", "follow", or "throw" (see javax.naming.Context.REFERRAL
+ for more information).
+ Microsoft Active Directory often returns referrals.
+ If you need to follow them set referrals to "follow".
+ Caution: if your DNS is not part of AD, the LDAP client lib might try
+ to resolve your domain name in DNS to find another LDAP server.
+
+
The base directory entry for performing role searches. If
not specified the top-level element in the directory context
diff --git a/webapps/docs/realm-howto.xml b/webapps/docs/realm-howto.xml
index cfd37ba1b..e2fdab74b 100644
--- a/webapps/docs/realm-howto.xml
+++ b/webapps/docs/realm-howto.xml
@@ -847,6 +847,15 @@ attributes are supported by this implementation:
"org.apache.catalina.realm.JNDIRealm" here.
+
+ Microsoft Active Directory often returns referrals.
+ When iterating over NamingEnumerations these lead to
+ PartialResultExceptions. If you want us to ignore those exceptions,
+ set this attribute to "true". Unfortunately there's no stable way
+ to detect, if the Exceptions really come from an AD referral.
+ The default value is "false".
+
+
If a socket connection can not be made to the provider at
the connectionURL an attempt will be made to use the
@@ -904,6 +913,16 @@ attributes are supported by this implementation:
specified
+
+ How do we handle JNDI referrals? Allowed values are
+ "ignore", "follow", or "throw" (see javax.naming.Context.REFERRAL
+ for more information).
+ Microsoft Active Directory often returns referrals.
+ If you need to follow them set referrals to "follow".
+ Caution: if your DNS is not part of AD, the LDAP client lib might try
+ to resolve your domain name in DNS to find another LDAP server.
+
+
A string specifying the security protocol to use. If not given
the providers default is used.
--
2.11.0