- Fixed CVEs: * CVE-2021-30640: Escape parameters in JNDI Realm queries (bsc#1188279) * CVE-2021-33037: Process T-E header from both HTTP 1.0 and HTTP 1.1. clients (bsc#1188278) * CVE-2021-41079: Validate incoming TLS packet (bsc#1190558) - Added patches: * tomcat-9.0-CVE-2021-30640.patch * tomcat-9.0-CVE-2021-33037.patch * tomcat-9.0-CVE-2021-41079.patch OBS-URL: https://build.opensuse.org/request/show/928113 OBS-URL: https://build.opensuse.org/package/show/Java:packages/tomcat?expand=0&rev=229
2667 lines
95 KiB
Diff
2667 lines
95 KiB
Diff
Index: apache-tomcat-9.0.43-src/build.properties.default
|
|
===================================================================
|
|
--- apache-tomcat-9.0.43-src.orig/build.properties.default
|
|
+++ apache-tomcat-9.0.43-src/build.properties.default
|
|
@@ -244,6 +244,15 @@ objenesis.home=${base.path}/objenesis-${
|
|
objenesis.jar=${objenesis.home}/objenesis-${objenesis.version}.jar
|
|
objenesis.loc=${base-maven.loc}/org/objenesis/objenesis/${objenesis.version}/objenesis-${objenesis.version}.jar
|
|
|
|
+# ----- UnboundID, used by unit tests, version 5.1.4 or later -----
|
|
+unboundid.version=5.1.4
|
|
+unboundid.checksum.enabled=true
|
|
+unboundid.checksum.algorithm=SHA-512
|
|
+unboundid.checksum.value=04cf7f59eddebdd5b51e5be55021f9d9c667cca6101eac954e7a8d5b51f4c23372cd8f041640157f082435a166b75d85e79252b516130ede7d966dae6d3eae67
|
|
+unboundid.home=${base.path}/unboundid-${unboundid.version}
|
|
+unboundid.jar=${unboundid.home}/unboundid-ldapsdk-${unboundid.version}.jar
|
|
+unboundid.loc=${base-maven.loc}/com/unboundid/unboundid-ldapsdk/${unboundid.version}/unboundid-ldapsdk-${unboundid.version}.jar
|
|
+
|
|
# ----- Checkstyle, version 6.16 or later -----
|
|
checkstyle.version=8.22
|
|
checkstyle.checksum.enabled=true
|
|
Index: apache-tomcat-9.0.43-src/build.xml
|
|
===================================================================
|
|
--- apache-tomcat-9.0.43-src.orig/build.xml
|
|
+++ apache-tomcat-9.0.43-src/build.xml
|
|
@@ -3164,6 +3164,15 @@ skip.installer property in build.propert
|
|
<param name="checksum.value" value="${objenesis.checksum.value}"/>
|
|
</antcall>
|
|
|
|
+ <antcall target="downloadfile">
|
|
+ <param name="sourcefile" value="${unboundid.loc}"/>
|
|
+ <param name="destfile" value="${unboundid.jar}"/>
|
|
+ <param name="destdir" value="${unboundid.home}"/>
|
|
+ <param name="checksum.enabled" value="${unboundid.checksum.enabled}"/>
|
|
+ <param name="checksum.algorithm" value="${unboundid.checksum.algorithm}"/>
|
|
+ <param name="checksum.value" value="${unboundid.checksum.value}"/>
|
|
+ </antcall>
|
|
+
|
|
</target>
|
|
|
|
<target name="download-cobertura"
|
|
Index: apache-tomcat-9.0.43-src/java/org/apache/catalina/realm/JNDIRealm.java
|
|
===================================================================
|
|
--- apache-tomcat-9.0.43-src.orig/java/org/apache/catalina/realm/JNDIRealm.java
|
|
+++ apache-tomcat-9.0.43-src/java/org/apache/catalina/realm/JNDIRealm.java
|
|
@@ -181,7 +181,6 @@ import org.ietf.jgss.GSSName;
|
|
*/
|
|
public class JNDIRealm extends RealmBase {
|
|
|
|
-
|
|
// ----------------------------------------------------- Instance Variables
|
|
|
|
/**
|
|
@@ -194,19 +193,16 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String connectionName = null;
|
|
|
|
-
|
|
/**
|
|
* The connection password for the server we will contact.
|
|
*/
|
|
protected String connectionPassword = null;
|
|
|
|
-
|
|
/**
|
|
* The connection URL for the server we will contact.
|
|
*/
|
|
protected String connectionURL = null;
|
|
|
|
-
|
|
/**
|
|
* The JNDI context factory used to acquire our InitialContext. By
|
|
* default, assumes use of an LDAP server using the standard JNDI LDAP
|
|
@@ -214,7 +210,6 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
|
|
|
|
-
|
|
/**
|
|
* How aliases should be dereferenced during search operations.
|
|
*/
|
|
@@ -226,14 +221,12 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
public static final String DEREF_ALIASES = "java.naming.ldap.derefAliases";
|
|
|
|
-
|
|
/**
|
|
* The protocol that will be used in the communication with the
|
|
* directory server.
|
|
*/
|
|
protected String protocol = null;
|
|
|
|
-
|
|
/**
|
|
* Should we ignore PartialResultExceptions when iterating over NamingEnumerations?
|
|
* Microsoft Active Directory often returns referrals, which lead
|
|
@@ -243,7 +236,6 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
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".
|
|
@@ -252,20 +244,17 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String referrals = null;
|
|
|
|
-
|
|
/**
|
|
* The base element for user searches.
|
|
*/
|
|
protected String userBase = "";
|
|
|
|
-
|
|
/**
|
|
* The message format used to search for a user, with "{0}" marking
|
|
* the spot where the username goes.
|
|
*/
|
|
protected String userSearch = null;
|
|
|
|
-
|
|
/**
|
|
* When searching for users, should the search be performed as the user
|
|
* currently being authenticated? If false, {@link #connectionName} and
|
|
@@ -274,13 +263,11 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
private boolean userSearchAsUser = false;
|
|
|
|
-
|
|
/**
|
|
* Should we search the entire subtree for matching users?
|
|
*/
|
|
protected boolean userSubtree = false;
|
|
|
|
-
|
|
/**
|
|
* The attribute name used to retrieve the user password.
|
|
*/
|
|
@@ -294,7 +281,6 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String userRoleAttribute = null;
|
|
|
|
-
|
|
/**
|
|
* A string of LDAP user patterns or paths, ":"-separated
|
|
* These will be used to form the distinguished name of a
|
|
@@ -305,7 +291,6 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String[] userPatternArray = null;
|
|
|
|
-
|
|
/**
|
|
* The message format used to form the distinguished name of a
|
|
* user, with "{0}" marking the spot where the specified username
|
|
@@ -313,26 +298,22 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String userPattern = null;
|
|
|
|
-
|
|
/**
|
|
* The base element for role searches.
|
|
*/
|
|
protected String roleBase = "";
|
|
|
|
-
|
|
/**
|
|
* The name of an attribute in the user's entry containing
|
|
* roles for that user
|
|
*/
|
|
protected String userRoleName = null;
|
|
|
|
-
|
|
/**
|
|
* The name of the attribute containing roles held elsewhere
|
|
*/
|
|
protected String roleName = null;
|
|
|
|
-
|
|
/**
|
|
* The message format used to select roles for a user, with "{0}" marking
|
|
* the spot where the distinguished name of the user goes. The "{1}"
|
|
@@ -340,7 +321,6 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String roleSearch = null;
|
|
|
|
-
|
|
/**
|
|
* Should we search the entire subtree for matching memberships?
|
|
*/
|
|
@@ -375,7 +355,6 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected String commonRole = null;
|
|
|
|
-
|
|
/**
|
|
* The timeout, in milliseconds, to use when trying to create a connection
|
|
* to the directory. The default is 5000 (5 seconds).
|
|
@@ -400,14 +379,12 @@ public class JNDIRealm extends RealmBase
|
|
*/
|
|
protected int timeLimit = 0;
|
|
|
|
-
|
|
/**
|
|
* Should delegated credentials from the SPNEGO authenticator be used if
|
|
* available
|
|
*/
|
|
protected boolean useDelegatedCredential = true;
|
|
|
|
-
|
|
/**
|
|
* The QOP that should be used for the connection to the LDAP server after
|
|
* authentication. This value is used to set the
|
|
@@ -465,31 +442,26 @@ public class JNDIRealm extends RealmBase
|
|
|
|
private boolean forceDnHexEscape = false;
|
|
|
|
-
|
|
/**
|
|
* Non pooled connection to our directory server.
|
|
*/
|
|
protected JNDIConnection singleConnection = new JNDIConnection();
|
|
|
|
-
|
|
/**
|
|
* The lock to ensure single connection thread safety.
|
|
*/
|
|
protected final Lock singleConnectionLock = new ReentrantLock();
|
|
|
|
-
|
|
/**
|
|
* Connection pool.
|
|
*/
|
|
protected SynchronizedStack<JNDIConnection> connectionPool = null;
|
|
|
|
-
|
|
/**
|
|
* The pool size limit. If 1, pooling is not used.
|
|
*/
|
|
protected int connectionPoolSize = 1;
|
|
|
|
-
|
|
/**
|
|
* Whether to use context ClassLoader or default ClassLoader.
|
|
* True means use context ClassLoader, and True is the default
|
|
@@ -504,37 +476,35 @@ public class JNDIRealm extends RealmBase
|
|
return forceDnHexEscape;
|
|
}
|
|
|
|
+
|
|
public void setForceDnHexEscape(boolean forceDnHexEscape) {
|
|
this.forceDnHexEscape = forceDnHexEscape;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the type of authentication to use.
|
|
*/
|
|
public String getAuthentication() {
|
|
-
|
|
return authentication;
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the type of authentication to use.
|
|
*
|
|
* @param authentication The authentication
|
|
*/
|
|
public void setAuthentication(String authentication) {
|
|
-
|
|
this.authentication = authentication;
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the connection username for this Realm.
|
|
*/
|
|
public String getConnectionName() {
|
|
-
|
|
return this.connectionName;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -544,9 +514,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param connectionName The new connection username
|
|
*/
|
|
public void setConnectionName(String connectionName) {
|
|
-
|
|
this.connectionName = connectionName;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -554,9 +522,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the connection password for this Realm.
|
|
*/
|
|
public String getConnectionPassword() {
|
|
-
|
|
return this.connectionPassword;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -566,9 +532,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param connectionPassword The new connection password
|
|
*/
|
|
public void setConnectionPassword(String connectionPassword) {
|
|
-
|
|
this.connectionPassword = connectionPassword;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -576,9 +540,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the connection URL for this Realm.
|
|
*/
|
|
public String getConnectionURL() {
|
|
-
|
|
return this.connectionURL;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -588,9 +550,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param connectionURL The new connection URL
|
|
*/
|
|
public void setConnectionURL(String connectionURL) {
|
|
-
|
|
this.connectionURL = connectionURL;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -598,9 +558,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the JNDI context factory for this Realm.
|
|
*/
|
|
public String getContextFactory() {
|
|
-
|
|
return this.contextFactory;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -610,11 +568,10 @@ public class JNDIRealm extends RealmBase
|
|
* @param contextFactory The new context factory
|
|
*/
|
|
public void setContextFactory(String contextFactory) {
|
|
-
|
|
this.contextFactory = contextFactory;
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the derefAliases setting to be used.
|
|
*/
|
|
@@ -622,33 +579,32 @@ public class JNDIRealm extends RealmBase
|
|
return derefAliases;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the value for derefAliases to be used when searching the directory.
|
|
*
|
|
* @param derefAliases New value of property derefAliases.
|
|
*/
|
|
public void setDerefAliases(java.lang.String derefAliases) {
|
|
- this.derefAliases = derefAliases;
|
|
+ this.derefAliases = derefAliases;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the protocol to be used.
|
|
*/
|
|
public String getProtocol() {
|
|
-
|
|
return protocol;
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the protocol for this Realm.
|
|
*
|
|
* @param protocol The new protocol.
|
|
*/
|
|
public void setProtocol(String protocol) {
|
|
-
|
|
this.protocol = protocol;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -692,9 +648,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the base element for user searches.
|
|
*/
|
|
public String getUserBase() {
|
|
-
|
|
return this.userBase;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -704,9 +658,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param userBase The new base element
|
|
*/
|
|
public void setUserBase(String userBase) {
|
|
-
|
|
this.userBase = userBase;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -714,9 +666,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the message format pattern for selecting users in this Realm.
|
|
*/
|
|
public String getUserSearch() {
|
|
-
|
|
return this.userSearch;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -745,9 +695,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the "search subtree for users" flag.
|
|
*/
|
|
public boolean getUserSubtree() {
|
|
-
|
|
return this.userSubtree;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -757,9 +705,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param userSubtree The new search flag
|
|
*/
|
|
public void setUserSubtree(boolean userSubtree) {
|
|
-
|
|
this.userSubtree = userSubtree;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -767,7 +713,6 @@ public class JNDIRealm extends RealmBase
|
|
* @return the user role name attribute name for this Realm.
|
|
*/
|
|
public String getUserRoleName() {
|
|
-
|
|
return userRoleName;
|
|
}
|
|
|
|
@@ -778,9 +723,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param userRoleName The new userRole name attribute name
|
|
*/
|
|
public void setUserRoleName(String userRoleName) {
|
|
-
|
|
this.userRoleName = userRoleName;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -788,9 +731,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the base element for role searches.
|
|
*/
|
|
public String getRoleBase() {
|
|
-
|
|
return this.roleBase;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -809,9 +750,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the role name attribute name for this Realm.
|
|
*/
|
|
public String getRoleName() {
|
|
-
|
|
return this.roleName;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -821,9 +760,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param roleName The new role name attribute name
|
|
*/
|
|
public void setRoleName(String roleName) {
|
|
-
|
|
this.roleName = roleName;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -831,9 +768,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the message format pattern for selecting roles in this Realm.
|
|
*/
|
|
public String getRoleSearch() {
|
|
-
|
|
return this.roleSearch;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -862,9 +797,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the "search subtree for roles" flag.
|
|
*/
|
|
public boolean getRoleSubtree() {
|
|
-
|
|
return this.roleSubtree;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -874,18 +807,15 @@ public class JNDIRealm extends RealmBase
|
|
* @param roleSubtree The new search flag
|
|
*/
|
|
public void setRoleSubtree(boolean roleSubtree) {
|
|
-
|
|
this.roleSubtree = roleSubtree;
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the "The nested group search flag" flag.
|
|
*/
|
|
public boolean getRoleNested() {
|
|
-
|
|
return this.roleNested;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -895,9 +825,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param roleNested The nested group search flag
|
|
*/
|
|
public void setRoleNested(boolean roleNested) {
|
|
-
|
|
this.roleNested = roleNested;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -905,9 +833,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the password attribute used to retrieve the user password.
|
|
*/
|
|
public String getUserPassword() {
|
|
-
|
|
return this.userPassword;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -917,9 +843,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param userPassword The new password attribute
|
|
*/
|
|
public void setUserPassword(String userPassword) {
|
|
-
|
|
this.userPassword = userPassword;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -927,6 +851,7 @@ public class JNDIRealm extends RealmBase
|
|
return userRoleAttribute;
|
|
}
|
|
|
|
+
|
|
public void setUserRoleAttribute(String userRoleAttribute) {
|
|
this.userRoleAttribute = userRoleAttribute;
|
|
}
|
|
@@ -935,14 +860,10 @@ public class JNDIRealm extends RealmBase
|
|
* @return the message format pattern for selecting users in this Realm.
|
|
*/
|
|
public String getUserPattern() {
|
|
-
|
|
return this.userPattern;
|
|
-
|
|
}
|
|
|
|
|
|
-
|
|
-
|
|
/**
|
|
* Set the message format pattern for selecting users in this Realm.
|
|
* This may be one simple pattern, or multiple patterns to be tried,
|
|
@@ -970,9 +891,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return Value of property alternateURL.
|
|
*/
|
|
public String getAlternateURL() {
|
|
-
|
|
return this.alternateURL;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -982,9 +901,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param alternateURL New value of property alternateURL.
|
|
*/
|
|
public void setAlternateURL(String alternateURL) {
|
|
-
|
|
this.alternateURL = alternateURL;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -992,9 +909,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the common role
|
|
*/
|
|
public String getCommonRole() {
|
|
-
|
|
return commonRole;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -1004,9 +919,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param commonRole The common role
|
|
*/
|
|
public void setCommonRole(String commonRole) {
|
|
-
|
|
this.commonRole = commonRole;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -1014,9 +927,7 @@ public class JNDIRealm extends RealmBase
|
|
* @return the connection timeout.
|
|
*/
|
|
public String getConnectionTimeout() {
|
|
-
|
|
return connectionTimeout;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -1026,18 +937,15 @@ public class JNDIRealm extends RealmBase
|
|
* @param timeout The new connection timeout
|
|
*/
|
|
public void setConnectionTimeout(String timeout) {
|
|
-
|
|
this.connectionTimeout = timeout;
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the read timeout.
|
|
*/
|
|
public String getReadTimeout() {
|
|
-
|
|
return readTimeout;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -1047,9 +955,7 @@ public class JNDIRealm extends RealmBase
|
|
* @param timeout The new read timeout
|
|
*/
|
|
public void setReadTimeout(String timeout) {
|
|
-
|
|
this.readTimeout = timeout;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -1077,6 +983,7 @@ public class JNDIRealm extends RealmBase
|
|
return useDelegatedCredential;
|
|
}
|
|
|
|
+
|
|
public void setUseDelegatedCredential(boolean useDelegatedCredential) {
|
|
this.useDelegatedCredential = useDelegatedCredential;
|
|
}
|
|
@@ -1086,6 +993,7 @@ public class JNDIRealm extends RealmBase
|
|
return spnegoDelegationQop;
|
|
}
|
|
|
|
+
|
|
public void setSpnegoDelegationQop(String spnegoDelegationQop) {
|
|
this.spnegoDelegationQop = spnegoDelegationQop;
|
|
}
|
|
@@ -1098,6 +1006,7 @@ public class JNDIRealm extends RealmBase
|
|
return useStartTls;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Flag whether StartTLS should be used when connecting to the ldap server
|
|
*
|
|
@@ -1109,6 +1018,7 @@ public class JNDIRealm extends RealmBase
|
|
this.useStartTls = useStartTls;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return list of the allowed cipher suites when connections are made using
|
|
* StartTLS
|
|
@@ -1128,6 +1038,7 @@ public class JNDIRealm extends RealmBase
|
|
return this.cipherSuitesArray;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the allowed cipher suites when opening a connection using StartTLS.
|
|
* The cipher suites are expected as a comma separated list.
|
|
@@ -1139,6 +1050,7 @@ public class JNDIRealm extends RealmBase
|
|
this.cipherSuites = suites;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the connection pool size, or the default value 1 if pooling
|
|
* is disabled
|
|
@@ -1147,6 +1059,7 @@ public class JNDIRealm extends RealmBase
|
|
return connectionPoolSize;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the connection pool size
|
|
* @param connectionPoolSize the new pool size
|
|
@@ -1155,6 +1068,7 @@ public class JNDIRealm extends RealmBase
|
|
this.connectionPoolSize = connectionPoolSize;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return name of the {@link HostnameVerifier} class used for connections
|
|
* using StartTLS, or the empty string, if the default verifier
|
|
@@ -1167,6 +1081,7 @@ public class JNDIRealm extends RealmBase
|
|
return this.hostnameVerifier.getClass().getCanonicalName();
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the {@link HostnameVerifier} to be used when opening connections
|
|
* using StartTLS. An instance of the given class name will be constructed
|
|
@@ -1183,6 +1098,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the {@link HostnameVerifier} to use for peer certificate
|
|
* verification when opening connections using StartTLS.
|
|
@@ -1191,8 +1107,7 @@ public class JNDIRealm extends RealmBase
|
|
if (this.hostnameVerifier != null) {
|
|
return this.hostnameVerifier;
|
|
}
|
|
- if (this.hostNameVerifierClassName == null
|
|
- || hostNameVerifierClassName.equals("")) {
|
|
+ if (this.hostNameVerifierClassName == null || hostNameVerifierClassName.equals("")) {
|
|
return null;
|
|
}
|
|
try {
|
|
@@ -1212,6 +1127,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the {@link SSLSocketFactory} to be used when opening connections
|
|
* using StartTLS. An instance of the factory with the given name will be
|
|
@@ -1225,6 +1141,7 @@ public class JNDIRealm extends RealmBase
|
|
this.sslSocketFactoryClassName = factoryClassName;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Set the ssl protocol to be used for connections using StartTLS.
|
|
*
|
|
@@ -1235,6 +1152,7 @@ public class JNDIRealm extends RealmBase
|
|
this.sslProtocol = protocol;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* @return the list of supported ssl protocols by the default
|
|
* {@link SSLContext}
|
|
@@ -1248,12 +1166,14 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
private Object constructInstance(String className)
|
|
throws ReflectiveOperationException {
|
|
Class<?> clazz = Class.forName(className);
|
|
return clazz.getConstructor().newInstance();
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Sets whether to use the context or default ClassLoader.
|
|
* True means use context ClassLoader.
|
|
@@ -1264,6 +1184,7 @@ public class JNDIRealm extends RealmBase
|
|
useContextClassLoader = useContext;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Returns whether to use the context or default ClassLoader.
|
|
* True means to use the context ClassLoader.
|
|
@@ -1274,6 +1195,7 @@ public class JNDIRealm extends RealmBase
|
|
return useContextClassLoader;
|
|
}
|
|
|
|
+
|
|
// ---------------------------------------------------------- Realm Methods
|
|
|
|
/**
|
|
@@ -1333,7 +1255,7 @@ public class JNDIRealm extends RealmBase
|
|
closePooledConnections();
|
|
|
|
// open a new directory context.
|
|
- connection = get();
|
|
+ connection = get(true);
|
|
|
|
// Try the authentication again.
|
|
principal = authenticate(connection, username, credentials);
|
|
@@ -1356,21 +1278,14 @@ public class JNDIRealm extends RealmBase
|
|
closePooledConnections();
|
|
|
|
// Return "not authenticated" for this request
|
|
- if (containerLog.isDebugEnabled())
|
|
+ if (containerLog.isDebugEnabled()) {
|
|
containerLog.debug("Returning null principal.");
|
|
+ }
|
|
return null;
|
|
-
|
|
}
|
|
-
|
|
}
|
|
|
|
|
|
- // -------------------------------------------------------- Package Methods
|
|
-
|
|
-
|
|
- // ------------------------------------------------------ Protected Methods
|
|
-
|
|
-
|
|
/**
|
|
* Return the Principal associated with the specified username and
|
|
* credentials, if there is one; otherwise return <code>null</code>.
|
|
@@ -1383,22 +1298,18 @@ public class JNDIRealm extends RealmBase
|
|
*
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- public Principal authenticate(JNDIConnection connection,
|
|
- String username,
|
|
- String credentials)
|
|
- throws NamingException {
|
|
-
|
|
- if (username == null || username.equals("")
|
|
- || credentials == null || credentials.equals("")) {
|
|
- if (containerLog.isDebugEnabled())
|
|
+ public Principal authenticate(JNDIConnection connection, String username, String credentials)
|
|
+ throws NamingException {
|
|
+
|
|
+ if (username == null || username.equals("") || credentials == null || credentials.equals("")) {
|
|
+ if (containerLog.isDebugEnabled()) {
|
|
containerLog.debug("username null or empty: returning null principal.");
|
|
+ }
|
|
return null;
|
|
}
|
|
|
|
if (userPatternArray != null) {
|
|
- for (int curUserPattern = 0;
|
|
- curUserPattern < userPatternArray.length;
|
|
- curUserPattern++) {
|
|
+ for (int curUserPattern = 0; curUserPattern < userPatternArray.length; curUserPattern++) {
|
|
// Retrieve user information
|
|
User user = getUser(connection, username, credentials, curUserPattern);
|
|
if (user != null) {
|
|
@@ -1426,12 +1337,14 @@ public class JNDIRealm extends RealmBase
|
|
} else {
|
|
// Retrieve user information
|
|
User user = getUser(connection, username, credentials);
|
|
- if (user == null)
|
|
+ if (user == null) {
|
|
return null;
|
|
+ }
|
|
|
|
// Check the user's credentials
|
|
- if (!checkCredentials(connection.context, user, credentials))
|
|
+ if (!checkCredentials(connection.context, user, credentials)) {
|
|
return null;
|
|
+ }
|
|
|
|
// Search for additional roles
|
|
List<String> roles = getRoles(connection, user);
|
|
@@ -1445,6 +1358,8 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
|
|
|
|
+ // ------------------------------------------------------ Protected Methods
|
|
+
|
|
/**
|
|
* Return a User object containing information about the user
|
|
* with the specified username, if found in the directory;
|
|
@@ -1457,9 +1372,7 @@ public class JNDIRealm extends RealmBase
|
|
*
|
|
* @see #getUser(JNDIConnection, String, String, int)
|
|
*/
|
|
- protected User getUser(JNDIConnection connection, String username)
|
|
- throws NamingException {
|
|
-
|
|
+ protected User getUser(JNDIConnection connection, String username) throws NamingException {
|
|
return getUser(connection, username, null, -1);
|
|
}
|
|
|
|
@@ -1477,9 +1390,7 @@ public class JNDIRealm extends RealmBase
|
|
*
|
|
* @see #getUser(JNDIConnection, String, String, int)
|
|
*/
|
|
- protected User getUser(JNDIConnection connection, String username, String credentials)
|
|
- throws NamingException {
|
|
-
|
|
+ protected User getUser(JNDIConnection connection, String username, String credentials) throws NamingException {
|
|
return getUser(connection, username, credentials, -1);
|
|
}
|
|
|
|
@@ -1502,18 +1413,19 @@ public class JNDIRealm extends RealmBase
|
|
* @return the User object
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected User getUser(JNDIConnection connection, String username,
|
|
- String credentials, int curUserPattern)
|
|
- throws NamingException {
|
|
+ protected User getUser(JNDIConnection connection, String username, String credentials, int curUserPattern)
|
|
+ throws NamingException {
|
|
|
|
User user = null;
|
|
|
|
// Get attributes to retrieve from user entry
|
|
List<String> list = new ArrayList<>();
|
|
- if (userPassword != null)
|
|
+ if (userPassword != null) {
|
|
list.add(userPassword);
|
|
- if (userRoleName != null)
|
|
+ }
|
|
+ if (userRoleName != null) {
|
|
list.add(userRoleName);
|
|
+ }
|
|
if (userRoleAttribute != null) {
|
|
list.add(userRoleAttribute);
|
|
}
|
|
@@ -1545,8 +1457,7 @@ public class JNDIRealm extends RealmBase
|
|
if (userPassword == null && credentials != null && user != null) {
|
|
// The password is available. Insert it since it may be required for
|
|
// role searches.
|
|
- return new User(user.getUserName(), user.getDN(), credentials,
|
|
- user.getRoles(), user.getUserRoleId());
|
|
+ return new User(user.getUserName(), user.getDN(), credentials, user.getRoles(), user.getUserRoleId());
|
|
}
|
|
|
|
return user;
|
|
@@ -1566,11 +1477,8 @@ public class JNDIRealm extends RealmBase
|
|
* @return the User object
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected User getUserByPattern(DirContext context,
|
|
- String username,
|
|
- String[] attrIds,
|
|
- String dn)
|
|
- throws NamingException {
|
|
+ protected User getUserByPattern(DirContext context, String username, String[] attrIds, String dn)
|
|
+ throws NamingException {
|
|
|
|
// If no attributes are requested, no need to look for them
|
|
if (attrIds == null || attrIds.length == 0) {
|
|
@@ -1584,13 +1492,15 @@ public class JNDIRealm extends RealmBase
|
|
} catch (NameNotFoundException e) {
|
|
return null;
|
|
}
|
|
- if (attrs == null)
|
|
+ if (attrs == null) {
|
|
return null;
|
|
+ }
|
|
|
|
// Retrieve value of userPassword
|
|
String password = null;
|
|
- if (userPassword != null)
|
|
+ if (userPassword != null) {
|
|
password = getAttributeValue(userPassword, attrs);
|
|
+ }
|
|
|
|
String userRoleAttrValue = null;
|
|
if (userRoleAttribute != null) {
|
|
@@ -1599,8 +1509,9 @@ public class JNDIRealm extends RealmBase
|
|
|
|
// Retrieve values of userRoleName attribute
|
|
ArrayList<String> roles = null;
|
|
- if (userRoleName != null)
|
|
+ if (userRoleName != null) {
|
|
roles = addAttributeValues(userRoleName, attrs, roles);
|
|
+ }
|
|
|
|
return new User(username, dn, password, roles, userRoleAttrValue);
|
|
}
|
|
@@ -1621,20 +1532,20 @@ public class JNDIRealm extends RealmBase
|
|
* @exception NamingException if a directory server error occurs
|
|
* @see #getUserByPattern(DirContext, String, String[], String)
|
|
*/
|
|
- protected User getUserByPattern(JNDIConnection connection,
|
|
- String username,
|
|
- String credentials,
|
|
- String[] attrIds,
|
|
- int curUserPattern)
|
|
- throws NamingException {
|
|
+ protected User getUserByPattern(JNDIConnection connection, String username, String credentials, String[] attrIds,
|
|
+ int curUserPattern) throws NamingException {
|
|
|
|
User user = null;
|
|
|
|
- if (username == null || userPatternArray[curUserPattern] == null)
|
|
+ if (username == null || userPatternArray[curUserPattern] == null) {
|
|
return null;
|
|
+ }
|
|
|
|
- // Form the dn from the user pattern
|
|
- String dn = connection.userPatternFormatArray[curUserPattern].format(new String[] { username });
|
|
+ // Form the DistinguishedName from the user pattern.
|
|
+ // Escape in case username contains a character with special meaning in
|
|
+ // an attribute value.
|
|
+ String dn = connection.userPatternFormatArray[curUserPattern].format(
|
|
+ new String[] { doAttributeValueEscaping(username) });
|
|
|
|
try {
|
|
user = getUserByPattern(connection.context, username, attrIds, dn);
|
|
@@ -1666,16 +1577,17 @@ public class JNDIRealm extends RealmBase
|
|
* @return the User object
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected User getUserBySearch(JNDIConnection connection,
|
|
- String username,
|
|
- String[] attrIds)
|
|
- throws NamingException {
|
|
+ protected User getUserBySearch(JNDIConnection connection, String username, String[] attrIds)
|
|
+ throws NamingException {
|
|
|
|
- if (username == null || connection.userSearchFormat == null)
|
|
+ if (username == null || connection.userSearchFormat == null) {
|
|
return null;
|
|
+ }
|
|
|
|
// Form the search filter
|
|
- String filter = connection.userSearchFormat.format(new String[] { username });
|
|
+ // Escape in case username contains a character with special meaning in
|
|
+ // a search filter.
|
|
+ String filter = connection.userSearchFormat.format(new String[] { doFilterEscaping(username) });
|
|
|
|
// Set up the search controls
|
|
SearchControls constraints = new SearchControls();
|
|
@@ -1690,12 +1602,12 @@ public class JNDIRealm extends RealmBase
|
|
constraints.setTimeLimit(timeLimit);
|
|
|
|
// Specify the attributes to be retrieved
|
|
- if (attrIds == null)
|
|
+ if (attrIds == null) {
|
|
attrIds = new String[0];
|
|
+ }
|
|
constraints.setReturningAttributes(attrIds);
|
|
|
|
- NamingEnumeration<SearchResult> results =
|
|
- connection.context.search(userBase, filter, constraints);
|
|
+ NamingEnumeration<SearchResult> results = connection.context.search(userBase, filter, constraints);
|
|
|
|
try {
|
|
// Fail if no entries found
|
|
@@ -1704,10 +1616,11 @@ public class JNDIRealm extends RealmBase
|
|
return null;
|
|
}
|
|
} catch (PartialResultException ex) {
|
|
- if (!adCompat)
|
|
+ if (!adCompat) {
|
|
throw ex;
|
|
- else
|
|
+ } else {
|
|
return null;
|
|
+ }
|
|
}
|
|
|
|
// Get result for the first entry found
|
|
@@ -1722,24 +1635,28 @@ public class JNDIRealm extends RealmBase
|
|
return null;
|
|
}
|
|
} catch (PartialResultException ex) {
|
|
- if (!adCompat)
|
|
+ if (!adCompat) {
|
|
throw ex;
|
|
+ }
|
|
}
|
|
|
|
String dn = getDistinguishedName(connection.context, userBase, result);
|
|
|
|
- if (containerLog.isTraceEnabled())
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
containerLog.trace(" entry found for " + username + " with dn " + dn);
|
|
+ }
|
|
|
|
// Get the entry's attributes
|
|
Attributes attrs = result.getAttributes();
|
|
- if (attrs == null)
|
|
+ if (attrs == null) {
|
|
return null;
|
|
+ }
|
|
|
|
// Retrieve value of userPassword
|
|
String password = null;
|
|
- if (userPassword != null)
|
|
+ if (userPassword != null) {
|
|
password = getAttributeValue(userPassword, attrs);
|
|
+ }
|
|
|
|
String userRoleAttrValue = null;
|
|
if (userRoleAttribute != null) {
|
|
@@ -1748,8 +1665,9 @@ public class JNDIRealm extends RealmBase
|
|
|
|
// Retrieve values of userRoleName attribute
|
|
ArrayList<String> roles = null;
|
|
- if (userRoleName != null)
|
|
+ if (userRoleName != null) {
|
|
roles = addAttributeValues(userRoleName, attrs, roles);
|
|
+ }
|
|
|
|
return new User(username, dn, password, roles, userRoleAttrValue);
|
|
} finally {
|
|
@@ -1775,30 +1693,25 @@ public class JNDIRealm extends RealmBase
|
|
* @return <code>true</code> if the credentials are validated
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected boolean checkCredentials(DirContext context,
|
|
- User user,
|
|
- String credentials)
|
|
- throws NamingException {
|
|
-
|
|
- boolean validated = false;
|
|
-
|
|
- if (userPassword == null) {
|
|
- validated = bindAsUser(context, user, credentials);
|
|
- } else {
|
|
- validated = compareCredentials(context, user, credentials);
|
|
- }
|
|
-
|
|
- if (containerLog.isTraceEnabled()) {
|
|
- if (validated) {
|
|
- containerLog.trace(sm.getString("jndiRealm.authenticateSuccess",
|
|
- user.getUserName()));
|
|
- } else {
|
|
- containerLog.trace(sm.getString("jndiRealm.authenticateFailure",
|
|
- user.getUserName()));
|
|
- }
|
|
- }
|
|
- return validated;
|
|
- }
|
|
+ protected boolean checkCredentials(DirContext context, User user, String credentials) throws NamingException {
|
|
+
|
|
+ boolean validated = false;
|
|
+
|
|
+ if (userPassword == null) {
|
|
+ validated = bindAsUser(context, user, credentials);
|
|
+ } else {
|
|
+ validated = compareCredentials(context, user, credentials);
|
|
+ }
|
|
+
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
+ if (validated) {
|
|
+ containerLog.trace(sm.getString("jndiRealm.authenticateSuccess", user.getUserName()));
|
|
+ } else {
|
|
+ containerLog.trace(sm.getString("jndiRealm.authenticateFailure", user.getUserName()));
|
|
+ }
|
|
+ }
|
|
+ return validated;
|
|
+ }
|
|
|
|
|
|
/**
|
|
@@ -1811,17 +1724,15 @@ public class JNDIRealm extends RealmBase
|
|
* @return <code>true</code> if the credentials are validated
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected boolean compareCredentials(DirContext context,
|
|
- User info,
|
|
- String credentials)
|
|
- throws NamingException {
|
|
-
|
|
+ protected boolean compareCredentials(DirContext context, User info, String credentials) throws NamingException {
|
|
// Validate the credentials specified by the user
|
|
- if (containerLog.isTraceEnabled())
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
containerLog.trace(" validating credentials");
|
|
+ }
|
|
|
|
- if (info == null || credentials == null)
|
|
+ if (info == null || credentials == null) {
|
|
return false;
|
|
+ }
|
|
|
|
String password = info.getPassword();
|
|
|
|
@@ -1838,21 +1749,22 @@ public class JNDIRealm extends RealmBase
|
|
* @return <code>true</code> if the credentials are validated
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected boolean bindAsUser(DirContext context,
|
|
- User user,
|
|
- String credentials)
|
|
- throws NamingException {
|
|
-
|
|
- if (credentials == null || user == null)
|
|
- return false;
|
|
-
|
|
- String dn = user.getDN();
|
|
- if (dn == null)
|
|
- return false;
|
|
-
|
|
- // Validate the credentials specified by the user
|
|
- if (containerLog.isTraceEnabled()) {
|
|
- containerLog.trace(" validating credentials by binding as the user");
|
|
+ protected boolean bindAsUser(DirContext context, User user, String credentials) throws NamingException {
|
|
+
|
|
+ if (credentials == null || user == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // This is returned from the directory so will be attribute value
|
|
+ // escaped if required
|
|
+ String dn = user.getDN();
|
|
+ if (dn == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // Validate the credentials specified by the user
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
+ containerLog.trace(" validating credentials by binding as the user");
|
|
}
|
|
|
|
userCredentialsAdd(context, dn, credentials);
|
|
@@ -1877,48 +1789,47 @@ public class JNDIRealm extends RealmBase
|
|
return validated;
|
|
}
|
|
|
|
- /**
|
|
- * Configure the context to use the provided credentials for
|
|
- * authentication.
|
|
- *
|
|
- * @param context DirContext to configure
|
|
- * @param dn Distinguished name of user
|
|
- * @param credentials Credentials of user
|
|
- * @exception NamingException if a directory server error occurs
|
|
- */
|
|
- private void userCredentialsAdd(DirContext context, String dn,
|
|
- String credentials) throws NamingException {
|
|
+
|
|
+ /**
|
|
+ * Configure the context to use the provided credentials for
|
|
+ * authentication.
|
|
+ *
|
|
+ * @param context DirContext to configure
|
|
+ * @param dn Distinguished name of user
|
|
+ * @param credentials Credentials of user
|
|
+ * @exception NamingException if a directory server error occurs
|
|
+ */
|
|
+ private void userCredentialsAdd(DirContext context, String dn, String credentials) throws NamingException {
|
|
// Set up security environment to bind as the user
|
|
context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
|
|
context.addToEnvironment(Context.SECURITY_CREDENTIALS, credentials);
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Configure the context to use {@link #connectionName} and
|
|
* {@link #connectionPassword} if specified or an anonymous connection if
|
|
* those attributes are not specified.
|
|
*
|
|
- * @param context DirContext to configure
|
|
- * @exception NamingException if a directory server error occurs
|
|
+ * @param context DirContext to configure
|
|
+ * @exception NamingException if a directory server error occurs
|
|
*/
|
|
- private void userCredentialsRemove(DirContext context)
|
|
- throws NamingException {
|
|
+ private void userCredentialsRemove(DirContext context) throws NamingException {
|
|
// Restore the original security environment
|
|
if (connectionName != null) {
|
|
- context.addToEnvironment(Context.SECURITY_PRINCIPAL,
|
|
- connectionName);
|
|
+ context.addToEnvironment(Context.SECURITY_PRINCIPAL, connectionName);
|
|
} else {
|
|
context.removeFromEnvironment(Context.SECURITY_PRINCIPAL);
|
|
}
|
|
|
|
if (connectionPassword != null) {
|
|
- context.addToEnvironment(Context.SECURITY_CREDENTIALS,
|
|
- connectionPassword);
|
|
+ context.addToEnvironment(Context.SECURITY_CREDENTIALS, connectionPassword);
|
|
} else {
|
|
context.removeFromEnvironment(Context.SECURITY_CREDENTIALS);
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Return a List of roles associated with the given User. Any
|
|
* roles present in the user's directory entry are supplemented by
|
|
@@ -1930,21 +1841,27 @@ public class JNDIRealm extends RealmBase
|
|
* @return the list of role names
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected List<String> getRoles(JNDIConnection connection, User user)
|
|
- throws NamingException {
|
|
+ protected List<String> getRoles(JNDIConnection connection, User user) throws NamingException {
|
|
|
|
- if (user == null)
|
|
+ if (user == null) {
|
|
return null;
|
|
+ }
|
|
|
|
+ // This is returned from the directory so will be attribute value
|
|
+ // escaped if required
|
|
String dn = user.getDN();
|
|
+ // This is the name the user provided to the authentication process so
|
|
+ // it will not be escaped
|
|
String username = user.getUserName();
|
|
String userRoleId = user.getUserRoleId();
|
|
|
|
- if (dn == null || username == null)
|
|
+ if (dn == null || username == null) {
|
|
return null;
|
|
+ }
|
|
|
|
- if (containerLog.isTraceEnabled())
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
containerLog.trace(" getRoles(" + dn + ")");
|
|
+ }
|
|
|
|
// Start with roles retrieved from the user entry
|
|
List<String> list = new ArrayList<>();
|
|
@@ -1952,8 +1869,9 @@ public class JNDIRealm extends RealmBase
|
|
if (userRoles != null) {
|
|
list.addAll(userRoles);
|
|
}
|
|
- if (commonRole != null)
|
|
+ if (commonRole != null) {
|
|
list.add(commonRole);
|
|
+ }
|
|
|
|
if (containerLog.isTraceEnabled()) {
|
|
containerLog.trace(" Found " + list.size() + " user internal roles");
|
|
@@ -1961,16 +1879,23 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
|
|
// Are we configured to do role searches?
|
|
- if ((connection.roleFormat == null) || (roleName == null))
|
|
+ if ((connection.roleFormat == null) || (roleName == null)) {
|
|
return list;
|
|
+ }
|
|
|
|
- // Set up parameters for an appropriate search
|
|
- String filter = connection.roleFormat.format(new String[] { doRFC2254Encoding(dn), username, userRoleId });
|
|
+ // Set up parameters for an appropriate search filter
|
|
+ // The dn is already attribute value escaped but the others are not
|
|
+ // This is a filter so all input will require filter escaping
|
|
+ String filter = connection.roleFormat.format(new String[] {
|
|
+ doFilterEscaping(dn),
|
|
+ doFilterEscaping(doAttributeValueEscaping(username)),
|
|
+ doFilterEscaping(doAttributeValueEscaping(userRoleId)) });
|
|
SearchControls controls = new SearchControls();
|
|
- if (roleSubtree)
|
|
+ if (roleSubtree) {
|
|
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
|
- else
|
|
+ } else {
|
|
controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
|
|
+ }
|
|
controls.setReturningAttributes(new String[] {roleName});
|
|
|
|
String base = null;
|
|
@@ -1979,7 +1904,9 @@ public class JNDIRealm extends RealmBase
|
|
Name name = np.parse(dn);
|
|
String nameParts[] = new String[name.size()];
|
|
for (int i = 0; i < name.size(); i++) {
|
|
- nameParts[i] = name.get(i);
|
|
+ // May have been returned with \<char> escaping rather than
|
|
+ // \<hex><hex>. Make sure it is \<hex><hex>.
|
|
+ nameParts[i] = convertToHexEscape(name.get(i));
|
|
}
|
|
base = connection.roleBaseFormat.format(nameParts);
|
|
} else {
|
|
@@ -1990,25 +1917,28 @@ public class JNDIRealm extends RealmBase
|
|
NamingEnumeration<SearchResult> results = searchAsUser(connection.context, user, base, filter, controls,
|
|
isRoleSearchAsUser());
|
|
|
|
- if (results == null)
|
|
+ if (results == null) {
|
|
return list; // Should never happen, but just in case ...
|
|
+ }
|
|
|
|
Map<String, String> groupMap = new HashMap<>();
|
|
try {
|
|
while (results.hasMore()) {
|
|
SearchResult result = results.next();
|
|
Attributes attrs = result.getAttributes();
|
|
- if (attrs == null)
|
|
+ if (attrs == null) {
|
|
continue;
|
|
- String dname = getDistinguishedName(connection.context, roleBase, result);
|
|
+ }
|
|
+ String dname = getDistinguishedName(connection.context, base, result);
|
|
String name = getAttributeValue(roleName, attrs);
|
|
if (name != null && dname != null) {
|
|
groupMap.put(dname, name);
|
|
}
|
|
}
|
|
} catch (PartialResultException ex) {
|
|
- if (!adCompat)
|
|
+ if (!adCompat) {
|
|
throw ex;
|
|
+ }
|
|
} finally {
|
|
results.close();
|
|
}
|
|
@@ -2033,22 +1963,28 @@ public class JNDIRealm extends RealmBase
|
|
Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration
|
|
|
|
for (Entry<String, String> group : newGroups.entrySet()) {
|
|
- filter = connection.roleFormat.format(new String[] { doRFC2254Encoding(group.getKey()),
|
|
- group.getValue(), group.getValue() });
|
|
+ // Group key is already value escaped if required
|
|
+ // Group value is not value escaped
|
|
+ // Everything needs to be filter escaped
|
|
+ filter = connection.roleFormat.format(new String[] {
|
|
+ doFilterEscaping(group.getKey()),
|
|
+ doFilterEscaping(doAttributeValueEscaping(group.getValue())),
|
|
+ doFilterEscaping(doAttributeValueEscaping(group.getValue())) });
|
|
|
|
if (containerLog.isTraceEnabled()) {
|
|
- containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
|
|
+ containerLog.trace("Perform a nested group search with base "+ roleBase +
|
|
+ " and filter " + filter);
|
|
}
|
|
|
|
- results = searchAsUser(connection.context, user, roleBase, filter, controls,
|
|
- isRoleSearchAsUser());
|
|
+ results = searchAsUser(connection.context, user, base, filter, controls, isRoleSearchAsUser());
|
|
|
|
try {
|
|
while (results.hasMore()) {
|
|
SearchResult result = results.next();
|
|
Attributes attrs = result.getAttributes();
|
|
- if (attrs == null)
|
|
+ if (attrs == null) {
|
|
continue;
|
|
+ }
|
|
String dname = getDistinguishedName(connection.context, roleBase, result);
|
|
String name = getAttributeValue(roleName, attrs);
|
|
if (name != null && dname != null && !groupMap.keySet().contains(dname)) {
|
|
@@ -2058,12 +1994,12 @@ public class JNDIRealm extends RealmBase
|
|
if (containerLog.isTraceEnabled()) {
|
|
containerLog.trace(" Found nested role " + dname + " -> " + name);
|
|
}
|
|
-
|
|
}
|
|
- }
|
|
+ }
|
|
} catch (PartialResultException ex) {
|
|
- if (!adCompat)
|
|
+ if (!adCompat) {
|
|
throw ex;
|
|
+ }
|
|
} finally {
|
|
results.close();
|
|
}
|
|
@@ -2077,6 +2013,7 @@ public class JNDIRealm extends RealmBase
|
|
return list;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Perform the search on the context as the {@code dn}, when
|
|
* {@code searchAsUser} is {@code true}, otherwise search the context with
|
|
@@ -2099,8 +2036,7 @@ public class JNDIRealm extends RealmBase
|
|
* @throws NamingException
|
|
* if a directory server error occurs
|
|
*/
|
|
- private NamingEnumeration<SearchResult> searchAsUser(DirContext context,
|
|
- User user, String base, String filter,
|
|
+ private NamingEnumeration<SearchResult> searchAsUser(DirContext context, User user, String base, String filter,
|
|
SearchControls controls, boolean searchAsUser) throws NamingException {
|
|
NamingEnumeration<SearchResult> results;
|
|
try {
|
|
@@ -2125,26 +2061,30 @@ public class JNDIRealm extends RealmBase
|
|
* @return the attribute value
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- private String getAttributeValue(String attrId, Attributes attrs)
|
|
- throws NamingException {
|
|
+ private String getAttributeValue(String attrId, Attributes attrs) throws NamingException {
|
|
|
|
- if (containerLog.isTraceEnabled())
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
containerLog.trace(" retrieving attribute " + attrId);
|
|
+ }
|
|
|
|
- if (attrId == null || attrs == null)
|
|
+ if (attrId == null || attrs == null) {
|
|
return null;
|
|
+ }
|
|
|
|
Attribute attr = attrs.get(attrId);
|
|
- if (attr == null)
|
|
+ if (attr == null) {
|
|
return null;
|
|
+ }
|
|
Object value = attr.get();
|
|
- if (value == null)
|
|
+ if (value == null) {
|
|
return null;
|
|
+ }
|
|
String valueString = null;
|
|
- if (value instanceof byte[])
|
|
+ if (value instanceof byte[]) {
|
|
valueString = new String((byte[]) value);
|
|
- else
|
|
+ } else {
|
|
valueString = value.toString();
|
|
+ }
|
|
|
|
return valueString;
|
|
}
|
|
@@ -2159,20 +2099,22 @@ public class JNDIRealm extends RealmBase
|
|
* @return the list of attribute values
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- private ArrayList<String> addAttributeValues(String attrId,
|
|
- Attributes attrs,
|
|
- ArrayList<String> values)
|
|
- throws NamingException{
|
|
+ private ArrayList<String> addAttributeValues(String attrId, Attributes attrs, ArrayList<String> values)
|
|
+ throws NamingException {
|
|
|
|
- if (containerLog.isTraceEnabled())
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
containerLog.trace(" retrieving values for attribute " + attrId);
|
|
- if (attrId == null || attrs == null)
|
|
+ }
|
|
+ if (attrId == null || attrs == null) {
|
|
return values;
|
|
- if (values == null)
|
|
+ }
|
|
+ if (values == null) {
|
|
values = new ArrayList<>();
|
|
+ }
|
|
Attribute attr = attrs.get(attrId);
|
|
- if (attr == null)
|
|
+ if (attr == null) {
|
|
return values;
|
|
+ }
|
|
NamingEnumeration<?> e = attr.getAll();
|
|
try {
|
|
while(e.hasMore()) {
|
|
@@ -2180,8 +2122,9 @@ public class JNDIRealm extends RealmBase
|
|
values.add(value);
|
|
}
|
|
} catch (PartialResultException ex) {
|
|
- if (!adCompat)
|
|
+ if (!adCompat) {
|
|
throw ex;
|
|
+ }
|
|
} finally {
|
|
e.close();
|
|
}
|
|
@@ -2214,8 +2157,9 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
// Close our opened connection
|
|
try {
|
|
- if (containerLog.isDebugEnabled())
|
|
+ if (containerLog.isDebugEnabled()) {
|
|
containerLog.debug("Closing directory context");
|
|
+ }
|
|
connection.context.close();
|
|
} catch (NamingException e) {
|
|
containerLog.error(sm.getString("jndiRealm.close"), e);
|
|
@@ -2225,9 +2169,9 @@ public class JNDIRealm extends RealmBase
|
|
if (connectionPool == null) {
|
|
singleConnectionLock.unlock();
|
|
}
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Close all pooled connections.
|
|
*/
|
|
@@ -2243,6 +2187,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Get the password for the specified user.
|
|
* @param username The user name
|
|
@@ -2258,7 +2203,6 @@ public class JNDIRealm extends RealmBase
|
|
JNDIConnection connection = null;
|
|
User user = null;
|
|
try {
|
|
-
|
|
// Ensure that we have a directory context available
|
|
connection = get();
|
|
|
|
@@ -2281,7 +2225,6 @@ public class JNDIRealm extends RealmBase
|
|
user = getUser(connection, username, null);
|
|
}
|
|
|
|
-
|
|
// Release this context
|
|
release(connection);
|
|
|
|
@@ -2292,15 +2235,14 @@ public class JNDIRealm extends RealmBase
|
|
// ... and have a password
|
|
return user.getPassword();
|
|
}
|
|
-
|
|
} catch (NamingException e) {
|
|
// Log the problem for posterity
|
|
containerLog.error(sm.getString("jndiRealm.exception"), e);
|
|
return null;
|
|
}
|
|
-
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Get the principal associated with the specified certificate.
|
|
* @param username The user name
|
|
@@ -2311,9 +2253,9 @@ public class JNDIRealm extends RealmBase
|
|
return getPrincipal(username, null);
|
|
}
|
|
|
|
+
|
|
@Override
|
|
- protected Principal getPrincipal(GSSName gssName,
|
|
- GSSCredential gssCredential) {
|
|
+ protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential) {
|
|
String name = gssName.toString();
|
|
|
|
if (isStripRealmForGss()) {
|
|
@@ -2327,15 +2269,14 @@ public class JNDIRealm extends RealmBase
|
|
return getPrincipal(name, gssCredential);
|
|
}
|
|
|
|
+
|
|
@Override
|
|
- protected Principal getPrincipal(String username,
|
|
- GSSCredential gssCredential) {
|
|
+ protected Principal getPrincipal(String username, GSSCredential gssCredential) {
|
|
|
|
JNDIConnection connection = null;
|
|
Principal principal = null;
|
|
|
|
try {
|
|
-
|
|
// Ensure that we have a directory context available
|
|
connection = get();
|
|
|
|
@@ -2347,7 +2288,6 @@ public class JNDIRealm extends RealmBase
|
|
principal = getPrincipal(connection, username, gssCredential);
|
|
|
|
} catch (CommunicationException | ServiceUnavailableException e) {
|
|
-
|
|
// log the exception so we know it's there.
|
|
containerLog.info(sm.getString("jndiRealm.exception.retry"), e);
|
|
|
|
@@ -2360,10 +2300,8 @@ public class JNDIRealm extends RealmBase
|
|
|
|
// Try the authentication again.
|
|
principal = getPrincipal(connection, username, gssCredential);
|
|
-
|
|
}
|
|
|
|
-
|
|
// Release this context
|
|
release(connection);
|
|
|
|
@@ -2371,16 +2309,12 @@ public class JNDIRealm extends RealmBase
|
|
return principal;
|
|
|
|
} catch (NamingException e) {
|
|
-
|
|
// Log the problem for posterity
|
|
containerLog.error(sm.getString("jndiRealm.exception"), e);
|
|
|
|
// Return "not authenticated" for this request
|
|
return null;
|
|
-
|
|
}
|
|
-
|
|
-
|
|
}
|
|
|
|
|
|
@@ -2392,9 +2326,8 @@ public class JNDIRealm extends RealmBase
|
|
* @return the Principal associated with the given certificate.
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected Principal getPrincipal(JNDIConnection connection,
|
|
- String username, GSSCredential gssCredential)
|
|
- throws NamingException {
|
|
+ protected Principal getPrincipal(JNDIConnection connection, String username, GSSCredential gssCredential)
|
|
+ throws NamingException {
|
|
|
|
User user = null;
|
|
List<String> roles = null;
|
|
@@ -2406,12 +2339,9 @@ public class JNDIRealm extends RealmBase
|
|
// Preserve the current context environment parameters
|
|
preservedEnvironment = context.getEnvironment();
|
|
// Set up context
|
|
- context.addToEnvironment(
|
|
- Context.SECURITY_AUTHENTICATION, "GSSAPI");
|
|
- context.addToEnvironment(
|
|
- "javax.security.sasl.server.authentication", "true");
|
|
- context.addToEnvironment(
|
|
- "javax.security.sasl.qop", spnegoDelegationQop);
|
|
+ context.addToEnvironment(Context.SECURITY_AUTHENTICATION, "GSSAPI");
|
|
+ context.addToEnvironment("javax.security.sasl.server.authentication", "true");
|
|
+ context.addToEnvironment("javax.security.sasl.qop", spnegoDelegationQop);
|
|
// Note: Subject already set in SPNEGO authenticator so no need
|
|
// for Subject.doAs() here
|
|
}
|
|
@@ -2421,23 +2351,20 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
} finally {
|
|
if (gssCredential != null && isUseDelegatedCredential()) {
|
|
- restoreEnvironmentParameter(context,
|
|
- Context.SECURITY_AUTHENTICATION, preservedEnvironment);
|
|
- restoreEnvironmentParameter(context,
|
|
- "javax.security.sasl.server.authentication", preservedEnvironment);
|
|
- restoreEnvironmentParameter(context, "javax.security.sasl.qop",
|
|
- preservedEnvironment);
|
|
+ restoreEnvironmentParameter(context, Context.SECURITY_AUTHENTICATION, preservedEnvironment);
|
|
+ restoreEnvironmentParameter(context, "javax.security.sasl.server.authentication", preservedEnvironment);
|
|
+ restoreEnvironmentParameter(context, "javax.security.sasl.qop", preservedEnvironment);
|
|
}
|
|
}
|
|
|
|
if (user != null) {
|
|
- return new GenericPrincipal(user.getUserName(), user.getPassword(),
|
|
- roles, null, null, gssCredential);
|
|
+ return new GenericPrincipal(user.getUserName(), user.getPassword(), roles, null, null, gssCredential);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
+
|
|
private void restoreEnvironmentParameter(DirContext context,
|
|
String parameterName, Hashtable<?, ?> preservedEnvironment) {
|
|
try {
|
|
@@ -2451,6 +2378,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Open (if necessary) and return a connection to the configured
|
|
* directory server for this Realm.
|
|
@@ -2458,12 +2386,28 @@ public class JNDIRealm extends RealmBase
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
protected JNDIConnection get() throws NamingException {
|
|
+ return get(false);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Open (if necessary) and return a connection to the configured
|
|
+ * directory server for this Realm.
|
|
+ * @param create when pooling, this forces creation of a new connection,
|
|
+ * for example after an error
|
|
+ * @return the connection
|
|
+ * @exception NamingException if a directory server error occurs
|
|
+ */
|
|
+ protected JNDIConnection get(boolean create) throws NamingException {
|
|
JNDIConnection connection = null;
|
|
// Use the pool if available, otherwise use the single connection
|
|
if (connectionPool != null) {
|
|
- connection = connectionPool.pop();
|
|
- if (connection == null) {
|
|
+ if (create) {
|
|
connection = create();
|
|
+ } else {
|
|
+ connection = connectionPool.pop();
|
|
+ if (connection == null) {
|
|
+ connection = create();
|
|
+ }
|
|
}
|
|
} else {
|
|
singleConnectionLock.lock();
|
|
@@ -2475,6 +2419,7 @@ public class JNDIRealm extends RealmBase
|
|
return connection;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Release our use of this connection so that it can be recycled.
|
|
*
|
|
@@ -2491,6 +2436,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Create a new connection wrapper, along with the
|
|
* message formats.
|
|
@@ -2505,8 +2451,7 @@ public class JNDIRealm extends RealmBase
|
|
int len = userPatternArray.length;
|
|
connection.userPatternFormatArray = new MessageFormat[len];
|
|
for (int i = 0; i < len; i++) {
|
|
- connection.userPatternFormatArray[i] =
|
|
- new MessageFormat(userPatternArray[i]);
|
|
+ connection.userPatternFormatArray[i] = new MessageFormat(userPatternArray[i]);
|
|
}
|
|
}
|
|
if (roleBase != null) {
|
|
@@ -2518,6 +2463,7 @@ public class JNDIRealm extends RealmBase
|
|
return connection;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Create a new connection to the directory server.
|
|
* @param connection The directory server connection wrapper
|
|
@@ -2552,12 +2498,14 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
@Override
|
|
public boolean isAvailable() {
|
|
// Simple best effort check
|
|
return (connectionPool != null || singleConnection.context != null);
|
|
}
|
|
|
|
+
|
|
private DirContext createDirContext(Hashtable<String, String> env) throws NamingException {
|
|
if (useStartTls) {
|
|
return createTlsDirContext(env);
|
|
@@ -2566,13 +2514,13 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
private SSLSocketFactory getSSLSocketFactory() {
|
|
if (sslSocketFactory != null) {
|
|
return sslSocketFactory;
|
|
}
|
|
final SSLSocketFactory result;
|
|
- if (this.sslSocketFactoryClassName != null
|
|
- && !sslSocketFactoryClassName.trim().equals("")) {
|
|
+ if (this.sslSocketFactoryClassName != null && !sslSocketFactoryClassName.trim().equals("")) {
|
|
result = createSSLSocketFactoryFromClassName(this.sslSocketFactoryClassName);
|
|
} else {
|
|
result = createSSLContextFactoryFromProtocol(sslProtocol);
|
|
@@ -2581,6 +2529,7 @@ public class JNDIRealm extends RealmBase
|
|
return result;
|
|
}
|
|
|
|
+
|
|
private SSLSocketFactory createSSLSocketFactoryFromClassName(String className) {
|
|
try {
|
|
Object o = constructInstance(className);
|
|
@@ -2598,6 +2547,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
private SSLSocketFactory createSSLContextFactoryFromProtocol(String protocol) {
|
|
try {
|
|
SSLContext sslContext;
|
|
@@ -2609,14 +2559,13 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
return sslContext.getSocketFactory();
|
|
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
|
- List<String> allowedProtocols = Arrays
|
|
- .asList(getSupportedSslProtocols());
|
|
- throw new IllegalArgumentException(
|
|
- sm.getString("jndiRealm.invalidSslProtocol", protocol,
|
|
- allowedProtocols), e);
|
|
+ List<String> allowedProtocols = Arrays.asList(getSupportedSslProtocols());
|
|
+ throw new IllegalArgumentException(sm.getString("jndiRealm.invalidSslProtocol",
|
|
+ protocol, allowedProtocols), e);
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Create a tls enabled LdapContext and set the StartTlsResponse tls
|
|
* instance variable.
|
|
@@ -2627,12 +2576,10 @@ public class JNDIRealm extends RealmBase
|
|
* @throws NamingException
|
|
* when something goes wrong while negotiating the connection
|
|
*/
|
|
- private DirContext createTlsDirContext(
|
|
- Hashtable<String, String> env) throws NamingException {
|
|
+ private DirContext createTlsDirContext(Hashtable<String, String> env) throws NamingException {
|
|
Map<String, Object> savedEnv = new HashMap<>();
|
|
- for (String key : Arrays.asList(Context.SECURITY_AUTHENTICATION,
|
|
- Context.SECURITY_CREDENTIALS, Context.SECURITY_PRINCIPAL,
|
|
- Context.SECURITY_PROTOCOL)) {
|
|
+ for (String key : Arrays.asList(Context.SECURITY_AUTHENTICATION, Context.SECURITY_CREDENTIALS,
|
|
+ Context.SECURITY_PRINCIPAL, Context.SECURITY_PROTOCOL)) {
|
|
Object entry = env.remove(key);
|
|
if (entry != null) {
|
|
savedEnv.put(key, entry);
|
|
@@ -2641,8 +2588,7 @@ public class JNDIRealm extends RealmBase
|
|
LdapContext result = null;
|
|
try {
|
|
result = new InitialLdapContext(env, null);
|
|
- tls = (StartTlsResponse) result
|
|
- .extendedOperation(new StartTlsRequest());
|
|
+ tls = (StartTlsResponse) result.extendedOperation(new StartTlsRequest());
|
|
if (getHostnameVerifier() != null) {
|
|
tls.setHostnameVerifier(getHostnameVerifier());
|
|
}
|
|
@@ -2651,22 +2597,21 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
try {
|
|
SSLSession negotiate = tls.negotiate(getSSLSocketFactory());
|
|
- containerLog.debug(sm.getString("jndiRealm.negotiatedTls",
|
|
- negotiate.getProtocol()));
|
|
+ containerLog.debug(sm.getString("jndiRealm.negotiatedTls", negotiate.getProtocol()));
|
|
} catch (IOException e) {
|
|
throw new NamingException(e.getMessage());
|
|
}
|
|
} finally {
|
|
if (result != null) {
|
|
for (Map.Entry<String, Object> savedEntry : savedEnv.entrySet()) {
|
|
- result.addToEnvironment(savedEntry.getKey(),
|
|
- savedEntry.getValue());
|
|
+ result.addToEnvironment(savedEntry.getKey(), savedEntry.getValue());
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Create our directory context configuration.
|
|
*
|
|
@@ -2677,40 +2622,48 @@ public class JNDIRealm extends RealmBase
|
|
Hashtable<String,String> env = new Hashtable<>();
|
|
|
|
// Configure our directory context environment.
|
|
- if (containerLog.isDebugEnabled() && connectionAttempt == 0)
|
|
+ if (containerLog.isDebugEnabled() && connectionAttempt == 0) {
|
|
containerLog.debug("Connecting to URL " + connectionURL);
|
|
- else if (containerLog.isDebugEnabled() && connectionAttempt > 0)
|
|
+ } else if (containerLog.isDebugEnabled() && connectionAttempt > 0) {
|
|
containerLog.debug("Connecting to URL " + alternateURL);
|
|
+ }
|
|
env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
|
|
- if (connectionName != null)
|
|
+ if (connectionName != null) {
|
|
env.put(Context.SECURITY_PRINCIPAL, connectionName);
|
|
- if (connectionPassword != null)
|
|
+ }
|
|
+ if (connectionPassword != null) {
|
|
env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
|
|
- if (connectionURL != null && connectionAttempt == 0)
|
|
+ }
|
|
+ if (connectionURL != null && connectionAttempt == 0) {
|
|
env.put(Context.PROVIDER_URL, connectionURL);
|
|
- else if (alternateURL != null && connectionAttempt > 0)
|
|
+ } else if (alternateURL != null && connectionAttempt > 0) {
|
|
env.put(Context.PROVIDER_URL, alternateURL);
|
|
- if (authentication != null)
|
|
+ }
|
|
+ if (authentication != null) {
|
|
env.put(Context.SECURITY_AUTHENTICATION, authentication);
|
|
- if (protocol != null)
|
|
+ }
|
|
+ if (protocol != null) {
|
|
env.put(Context.SECURITY_PROTOCOL, protocol);
|
|
- if (referrals != null)
|
|
+ }
|
|
+ if (referrals != null) {
|
|
env.put(Context.REFERRAL, referrals);
|
|
- if (derefAliases != null)
|
|
+ }
|
|
+ if (derefAliases != null) {
|
|
env.put(JNDIRealm.DEREF_ALIASES, derefAliases);
|
|
- if (connectionTimeout != null)
|
|
+ }
|
|
+ if (connectionTimeout != null) {
|
|
env.put("com.sun.jndi.ldap.connect.timeout", connectionTimeout);
|
|
- if (readTimeout != null)
|
|
+ }
|
|
+ if (readTimeout != null) {
|
|
env.put("com.sun.jndi.ldap.read.timeout", readTimeout);
|
|
+ }
|
|
|
|
return env;
|
|
-
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------ Lifecycle Methods
|
|
|
|
-
|
|
/**
|
|
* Prepare for the beginning of active use of the public methods of this
|
|
* component and implement the requirements of
|
|
@@ -2752,7 +2705,7 @@ public class JNDIRealm extends RealmBase
|
|
* @exception LifecycleException if this component detects a fatal error
|
|
* that needs to be reported
|
|
*/
|
|
- @Override
|
|
+ @Override
|
|
protected void stopInternal() throws LifecycleException {
|
|
super.stopInternal();
|
|
// Close any open directory server connection
|
|
@@ -2765,6 +2718,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Given a string containing LDAP patterns for user locations (separated by
|
|
* parentheses in a pseudo-LDAP search string format -
|
|
@@ -2799,8 +2753,7 @@ public class JNDIRealm extends RealmBase
|
|
while (userPatternString.charAt(endParenLoc - 1) == '\\') {
|
|
endParenLoc = userPatternString.indexOf(')', endParenLoc+1);
|
|
}
|
|
- String nextPathPart = userPatternString.substring
|
|
- (startParenLoc+1, endParenLoc);
|
|
+ String nextPathPart = userPatternString.substring(startParenLoc+1, endParenLoc);
|
|
pathList.add(nextPathPart);
|
|
startingPoint = endParenLoc+1;
|
|
startParenLoc = userPatternString.indexOf('(', startingPoint);
|
|
@@ -2808,7 +2761,6 @@ public class JNDIRealm extends RealmBase
|
|
return pathList.toArray(new String[] {});
|
|
}
|
|
return null;
|
|
-
|
|
}
|
|
|
|
|
|
@@ -2823,10 +2775,36 @@ public class JNDIRealm extends RealmBase
|
|
* ) -> \29
|
|
* \ -> \5c
|
|
* \0 -> \00
|
|
+ *
|
|
* @param inString string to escape according to RFC 2254 guidelines
|
|
+ *
|
|
* @return String the escaped/encoded result
|
|
+ *
|
|
+ * @deprecated Will be removed in Tomcat 10.1.x onwards
|
|
*/
|
|
+ @Deprecated
|
|
protected String doRFC2254Encoding(String inString) {
|
|
+ return doFilterEscaping(inString);
|
|
+ }
|
|
+
|
|
+
|
|
+ /**
|
|
+ * Given an LDAP search string, returns the string with certain characters
|
|
+ * escaped according to RFC 2254 guidelines.
|
|
+ * The character mapping is as follows:
|
|
+ * char -> Replacement
|
|
+ * ---------------------------
|
|
+ * * -> \2a
|
|
+ * ( -> \28
|
|
+ * ) -> \29
|
|
+ * \ -> \5c
|
|
+ * \0 -> \00
|
|
+ *
|
|
+ * @param inString string to escape according to RFC 2254 guidelines
|
|
+ *
|
|
+ * @return String the escaped/encoded result
|
|
+ */
|
|
+ protected String doFilterEscaping(String inString) {
|
|
StringBuilder buf = new StringBuilder(inString.length());
|
|
for (int i = 0; i < inString.length(); i++) {
|
|
char c = inString.charAt(i);
|
|
@@ -2864,47 +2842,42 @@ public class JNDIRealm extends RealmBase
|
|
* @return String containing the distinguished name
|
|
* @exception NamingException if a directory server error occurs
|
|
*/
|
|
- protected String getDistinguishedName(DirContext context, String base,
|
|
- SearchResult result) throws NamingException {
|
|
+ protected String getDistinguishedName(DirContext context, String base, SearchResult result) throws NamingException {
|
|
// Get the entry's distinguished name. For relative results, this means
|
|
// we need to composite a name with the base name, the context name, and
|
|
// the result name. For non-relative names, use the returned name.
|
|
String resultName = result.getName();
|
|
Name name;
|
|
if (result.isRelative()) {
|
|
- if (containerLog.isTraceEnabled()) {
|
|
- containerLog.trace(" search returned relative name: " + resultName);
|
|
- }
|
|
- NameParser parser = context.getNameParser("");
|
|
- Name contextName = parser.parse(context.getNameInNamespace());
|
|
- Name baseName = parser.parse(base);
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
+ containerLog.trace(" search returned relative name: " + resultName);
|
|
+ }
|
|
+ NameParser parser = context.getNameParser("");
|
|
+ Name contextName = parser.parse(context.getNameInNamespace());
|
|
+ Name baseName = parser.parse(base);
|
|
|
|
- // Bugzilla 32269
|
|
- Name entryName = parser.parse(new CompositeName(resultName).get(0));
|
|
+ // Bugzilla 32269
|
|
+ Name entryName = parser.parse(new CompositeName(resultName).get(0));
|
|
|
|
- name = contextName.addAll(baseName);
|
|
- name = name.addAll(entryName);
|
|
+ name = contextName.addAll(baseName);
|
|
+ name = name.addAll(entryName);
|
|
} else {
|
|
- if (containerLog.isTraceEnabled()) {
|
|
- containerLog.trace(" search returned absolute name: " + resultName);
|
|
- }
|
|
- try {
|
|
- // Normalize the name by running it through the name parser.
|
|
- NameParser parser = context.getNameParser("");
|
|
- URI userNameUri = new URI(resultName);
|
|
- String pathComponent = userNameUri.getPath();
|
|
- // Should not ever have an empty path component, since that is /{DN}
|
|
- if (pathComponent.length() < 1 ) {
|
|
- throw new InvalidNameException(
|
|
- "Search returned unparseable absolute name: " +
|
|
- resultName );
|
|
- }
|
|
- name = parser.parse(pathComponent.substring(1));
|
|
- } catch ( URISyntaxException e ) {
|
|
- throw new InvalidNameException(
|
|
- "Search returned unparseable absolute name: " +
|
|
- resultName );
|
|
- }
|
|
+ if (containerLog.isTraceEnabled()) {
|
|
+ containerLog.trace(" search returned absolute name: " + resultName);
|
|
+ }
|
|
+ try {
|
|
+ // Normalize the name by running it through the name parser.
|
|
+ NameParser parser = context.getNameParser("");
|
|
+ URI userNameUri = new URI(resultName);
|
|
+ String pathComponent = userNameUri.getPath();
|
|
+ // Should not ever have an empty path component, since that is /{DN}
|
|
+ if (pathComponent.length() < 1 ) {
|
|
+ throw new InvalidNameException("Search returned unparseable absolute name: " + resultName);
|
|
+ }
|
|
+ name = parser.parse(pathComponent.substring(1));
|
|
+ } catch ( URISyntaxException e ) {
|
|
+ throw new InvalidNameException("Search returned unparseable absolute name: " + resultName);
|
|
+ }
|
|
}
|
|
|
|
if (getForceDnHexEscape()) {
|
|
@@ -2916,6 +2889,78 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
|
|
|
|
+ /**
|
|
+ * Implements the necessary escaping to represent an attribute value as a
|
|
+ * String as per RFC 4514.
|
|
+ *
|
|
+ * @param input The original attribute value
|
|
+ * @return The string representation of the attribute value
|
|
+ */
|
|
+ protected String doAttributeValueEscaping(String input) {
|
|
+ int len = input.length();
|
|
+ StringBuilder result = new StringBuilder();
|
|
+
|
|
+ for (int i = 0; i < len; i++) {
|
|
+ char c = input.charAt(i);
|
|
+ switch (c) {
|
|
+ case ' ': {
|
|
+ if (i == 0 || i == (len -1)) {
|
|
+ result.append("\\20");
|
|
+ } else {
|
|
+ result.append(c);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case '#': {
|
|
+ if (i == 0 ) {
|
|
+ result.append("\\23");
|
|
+ } else {
|
|
+ result.append(c);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case '\"': {
|
|
+ result.append("\\22");
|
|
+ break;
|
|
+ }
|
|
+ case '+': {
|
|
+ result.append("\\2B");
|
|
+ break;
|
|
+ }
|
|
+ case ',': {
|
|
+ result.append("\\2C");
|
|
+ break;
|
|
+ }
|
|
+ case ';': {
|
|
+ result.append("\\3B");
|
|
+ break;
|
|
+ }
|
|
+ case '<': {
|
|
+ result.append("\\3C");
|
|
+ break;
|
|
+ }
|
|
+ case '>': {
|
|
+ result.append("\\3E");
|
|
+ break;
|
|
+ }
|
|
+ case '\\': {
|
|
+ result.append("\\5C");
|
|
+ break;
|
|
+ }
|
|
+ case '\u0000': {
|
|
+ result.append("\\00");
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ result.append(c);
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ return result.toString();
|
|
+ }
|
|
+
|
|
+
|
|
protected static String convertToHexEscape(String input) {
|
|
if (input.indexOf('\\') == -1) {
|
|
// No escaping present. Return original.
|
|
@@ -2992,7 +3037,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
|
|
|
|
- // ------------------------------------------------------ Private Classes
|
|
+ // ------------------------------------------------------ Protected Classes
|
|
|
|
/**
|
|
* A protected class representing a User
|
|
@@ -3005,9 +3050,7 @@ public class JNDIRealm extends RealmBase
|
|
private final List<String> roles;
|
|
private final String userRoleId;
|
|
|
|
-
|
|
- public User(String username, String dn, String password,
|
|
- List<String> roles, String userRoleId) {
|
|
+ public User(String username, String dn, String password, List<String> roles, String userRoleId) {
|
|
this.username = username;
|
|
this.dn = dn;
|
|
this.password = password;
|
|
@@ -3040,6 +3083,7 @@ public class JNDIRealm extends RealmBase
|
|
}
|
|
}
|
|
|
|
+
|
|
/**
|
|
* Class holding the connection to the directory plus the associated
|
|
* non thread safe message formats.
|
|
@@ -3074,8 +3118,5 @@ public class JNDIRealm extends RealmBase
|
|
* The directory context linking us to our directory server.
|
|
*/
|
|
protected DirContext context = null;
|
|
-
|
|
}
|
|
-
|
|
}
|
|
-
|
|
Index: apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
|
|
@@ -0,0 +1,88 @@
|
|
+
|
|
+@@ -0,0 +1,86 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.catalina.realm;
|
|
+
|
|
+import java.util.ArrayList;
|
|
+import java.util.Collection;
|
|
+import java.util.List;
|
|
+
|
|
+import org.junit.Assert;
|
|
+import org.junit.Test;
|
|
+import org.junit.runner.RunWith;
|
|
+import org.junit.runners.Parameterized;
|
|
+import org.junit.runners.Parameterized.Parameter;
|
|
+
|
|
+@RunWith(Parameterized.class)
|
|
+public class TestJNDIRealmAttributeValueEscape {
|
|
+
|
|
+ @Parameterized.Parameters(name = "{index}: in[{0}], out[{1}]")
|
|
+ public static Collection<Object[]> parameters() {
|
|
+ List<Object[]> parameterSets = new ArrayList<>();
|
|
+
|
|
+ // No escaping required
|
|
+ parameterSets.add(new String[] { "none", "none" });
|
|
+ // Simple cases (same order as RFC 4512 section 2)
|
|
+ // Each appearing at the beginning, middle and ent
|
|
+ parameterSets.add(new String[] { " test", "\\20test" });
|
|
+ parameterSets.add(new String[] { "te st", "te st" });
|
|
+ parameterSets.add(new String[] { "test ", "test\\20" });
|
|
+ parameterSets.add(new String[] { "#test", "\\23test" });
|
|
+ parameterSets.add(new String[] { "te#st", "te#st" });
|
|
+ parameterSets.add(new String[] { "test#", "test#" });
|
|
+ parameterSets.add(new String[] { "\"test", "\\22test" });
|
|
+ parameterSets.add(new String[] { "te\"st", "te\\22st" });
|
|
+ parameterSets.add(new String[] { "test\"", "test\\22" });
|
|
+ parameterSets.add(new String[] { "+test", "\\2Btest" });
|
|
+ parameterSets.add(new String[] { "te+st", "te\\2Bst" });
|
|
+ parameterSets.add(new String[] { "test+", "test\\2B" });
|
|
+ parameterSets.add(new String[] { ",test", "\\2Ctest" });
|
|
+ parameterSets.add(new String[] { "te,st", "te\\2Cst" });
|
|
+ parameterSets.add(new String[] { "test,", "test\\2C" });
|
|
+ parameterSets.add(new String[] { ";test", "\\3Btest" });
|
|
+ parameterSets.add(new String[] { "te;st", "te\\3Bst" });
|
|
+ parameterSets.add(new String[] { "test;", "test\\3B" });
|
|
+ parameterSets.add(new String[] { "<test", "\\3Ctest" });
|
|
+ parameterSets.add(new String[] { "te<st", "te\\3Cst" });
|
|
+ parameterSets.add(new String[] { "test<", "test\\3C" });
|
|
+ parameterSets.add(new String[] { ">test", "\\3Etest" });
|
|
+ parameterSets.add(new String[] { "te>st", "te\\3Est" });
|
|
+ parameterSets.add(new String[] { "test>", "test\\3E" });
|
|
+ parameterSets.add(new String[] { "\\test", "\\5Ctest" });
|
|
+ parameterSets.add(new String[] { "te\\st", "te\\5Cst" });
|
|
+ parameterSets.add(new String[] { "test\\", "test\\5C" });
|
|
+ parameterSets.add(new String[] { "\u0000test", "\\00test" });
|
|
+ parameterSets.add(new String[] { "te\u0000st", "te\\00st" });
|
|
+ parameterSets.add(new String[] { "test\u0000", "test\\00" });
|
|
+ return parameterSets;
|
|
+ }
|
|
+
|
|
+
|
|
+ @Parameter(0)
|
|
+ public String in;
|
|
+ @Parameter(1)
|
|
+ public String out;
|
|
+
|
|
+ private JNDIRealm realm = new JNDIRealm();
|
|
+
|
|
+ @Test
|
|
+ public void testConvertToHexEscape() throws Exception {
|
|
+ String result = realm.doAttributeValueEscaping(in);
|
|
+ Assert.assertEquals(out, result);
|
|
+ }
|
|
+}
|
|
Index: apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ apache-tomcat-9.0.43-src/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java
|
|
@@ -0,0 +1,263 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.catalina.realm;
|
|
+
|
|
+import java.util.ArrayList;
|
|
+import java.util.Arrays;
|
|
+import java.util.Collection;
|
|
+import java.util.HashSet;
|
|
+import java.util.List;
|
|
+import java.util.Set;
|
|
+
|
|
+import org.junit.AfterClass;
|
|
+import org.junit.Assert;
|
|
+import org.junit.BeforeClass;
|
|
+import org.junit.Test;
|
|
+import org.junit.runner.RunWith;
|
|
+import org.junit.runners.Parameterized;
|
|
+import org.junit.runners.Parameterized.Parameter;
|
|
+
|
|
+import org.apache.juli.logging.LogFactory;
|
|
+
|
|
+import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
|
+import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
|
|
+import com.unboundid.ldap.sdk.AddRequest;
|
|
+import com.unboundid.ldap.sdk.LDAPConnection;
|
|
+import com.unboundid.ldap.sdk.LDAPResult;
|
|
+import com.unboundid.ldap.sdk.ResultCode;
|
|
+
|
|
+@RunWith(Parameterized.class)
|
|
+public class TestJNDIRealmIntegration {
|
|
+
|
|
+ private static final String USER_PATTERN = "cn={0},ou=people,dc=example,dc=com";
|
|
+ private static final String USER_SEARCH = "cn={0}";
|
|
+ private static final String USER_BASE = "ou=people,dc=example,dc=com";
|
|
+ private static final String ROLE_SEARCH_A = "member={0}";
|
|
+ private static final String ROLE_SEARCH_B = "member=cn={1},ou=people,dc=example,dc=com";
|
|
+ private static final String ROLE_SEARCH_C = "member=cn={2},ou=people,dc=example,dc=com";
|
|
+ private static final String ROLE_BASE = "ou=people,dc=example,dc=com";
|
|
+
|
|
+ private static InMemoryDirectoryServer ldapServer;
|
|
+
|
|
+ @Parameterized.Parameters(name = "{index}: user[{5}], pwd[{6}]")
|
|
+ public static Collection<Object[]> parameters() {
|
|
+ List<Object[]> parameterSets = new ArrayList<>();
|
|
+ for (String roleSearch : new String[] { ROLE_SEARCH_A, ROLE_SEARCH_B, ROLE_SEARCH_C }) {
|
|
+ addUsers(USER_PATTERN, null, null, roleSearch, ROLE_BASE, parameterSets);
|
|
+ addUsers(null, USER_SEARCH, USER_BASE, roleSearch, ROLE_BASE, parameterSets);
|
|
+ }
|
|
+ parameterSets.add(new Object[] { "cn={0},ou=s\\;ub,ou=people,dc=example,dc=com", null, null, ROLE_SEARCH_A,
|
|
+ "{3},ou=people,dc=example,dc=com", "testsub", "test", new String[] {"TestGroup4"} });
|
|
+ return parameterSets;
|
|
+ }
|
|
+
|
|
+
|
|
+ private static void addUsers(String userPattern, String userSearch, String userBase, String roleSearch,
|
|
+ String roleBase, List<Object[]> parameterSets) {
|
|
+ parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase,
|
|
+ "test", "test", new String[] {"TestGroup"} });
|
|
+ parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase,
|
|
+ "t;", "test", new String[] {"TestGroup"} });
|
|
+ parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase,
|
|
+ "t*", "test", new String[] {"TestGroup"} });
|
|
+ parameterSets.add(new Object[] { userPattern, userSearch, userBase, roleSearch, roleBase,
|
|
+ "t=", "test", new String[] {"Test<Group*2", "Test>Group*3"} });
|
|
+ }
|
|
+
|
|
+
|
|
+ @Parameter(0)
|
|
+ public String realmConfigUserPattern;
|
|
+ @Parameter(1)
|
|
+ public String realmConfigUserSearch;
|
|
+ @Parameter(2)
|
|
+ public String realmConfigUserBase;
|
|
+ @Parameter(3)
|
|
+ public String realmConfigRoleSearch;
|
|
+ @Parameter(4)
|
|
+ public String realmConfigRoleBase;
|
|
+ @Parameter(5)
|
|
+ public String username;
|
|
+ @Parameter(6)
|
|
+ public String credentials;
|
|
+ @Parameter(7)
|
|
+ public String[] groups;
|
|
+
|
|
+ @Test
|
|
+ public void testAuthenication() throws Exception {
|
|
+ JNDIRealm realm = new JNDIRealm();
|
|
+ realm.containerLog = LogFactory.getLog(TestJNDIRealmIntegration.class);
|
|
+
|
|
+ realm.setConnectionURL("ldap://localhost:" + ldapServer.getListenPort());
|
|
+ realm.setUserPattern(realmConfigUserPattern);
|
|
+ realm.setUserSearch(realmConfigUserSearch);
|
|
+ realm.setUserBase(realmConfigUserBase);
|
|
+ realm.setUserRoleAttribute("cn");
|
|
+ realm.setRoleName("cn");
|
|
+ realm.setRoleBase(realmConfigRoleBase);
|
|
+ realm.setRoleSearch(realmConfigRoleSearch);
|
|
+ realm.setRoleNested(true);
|
|
+
|
|
+ GenericPrincipal p = (GenericPrincipal) realm.authenticate(username, credentials);
|
|
+
|
|
+ Assert.assertNotNull(p);
|
|
+ Assert.assertEquals(username, p.name);
|
|
+
|
|
+ Set<String> actualGroups = new HashSet<>(Arrays.asList(p.getRoles()));
|
|
+ Set<String> expectedGroups = new HashSet<>(Arrays.asList(groups));
|
|
+
|
|
+ Assert.assertEquals(expectedGroups.size(), actualGroups.size());
|
|
+ Set<String> tmp = new HashSet<>();
|
|
+ tmp.addAll(expectedGroups);
|
|
+ tmp.removeAll(actualGroups);
|
|
+ Assert.assertEquals(0, tmp.size());
|
|
+ }
|
|
+
|
|
+
|
|
+ @BeforeClass
|
|
+ public static void createLDAP() throws Exception {
|
|
+ InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com");
|
|
+ config.addAdditionalBindCredentials("cn=admin", "password");
|
|
+ ldapServer = new InMemoryDirectoryServer(config);
|
|
+
|
|
+ ldapServer.startListening();
|
|
+
|
|
+ try (LDAPConnection conn = ldapServer.getConnection()) {
|
|
+
|
|
+ // Note: Only the DNs need attribute value escaping
|
|
+ AddRequest addBase = new AddRequest(
|
|
+ "dn: dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: domain",
|
|
+ "dc: example");
|
|
+ LDAPResult result = conn.processOperation(addBase);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addPeople = new AddRequest(
|
|
+ "dn: ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: organizationalUnit");
|
|
+ result = conn.processOperation(addPeople);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addUserTest = new AddRequest(
|
|
+ "dn: cn=test,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: person",
|
|
+ "objectClass: organizationalPerson",
|
|
+ "cn: test",
|
|
+ "sn: Test",
|
|
+ "userPassword: test");
|
|
+ result = conn.processOperation(addUserTest);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addUserTestSemicolon = new AddRequest(
|
|
+ "dn: cn=t\\;,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: person",
|
|
+ "objectClass: organizationalPerson",
|
|
+ "cn: t;",
|
|
+ "sn: Tsemicolon",
|
|
+ "userPassword: test");
|
|
+ result = conn.processOperation(addUserTestSemicolon);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addUserTestAsterisk = new AddRequest(
|
|
+ "dn: cn=t*,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: person",
|
|
+ "objectClass: organizationalPerson",
|
|
+ "cn: t*",
|
|
+ "sn: Tasterisk",
|
|
+ "userPassword: test");
|
|
+ result = conn.processOperation(addUserTestAsterisk);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addUserTestEquals = new AddRequest(
|
|
+ "dn: cn=t\\=,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: person",
|
|
+ "objectClass: organizationalPerson",
|
|
+ "cn: t=",
|
|
+ "sn: Tequals",
|
|
+ "userPassword: test");
|
|
+ result = conn.processOperation(addUserTestEquals);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addGroupTest = new AddRequest(
|
|
+ "dn: cn=TestGroup,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: groupOfNames",
|
|
+ "cn: TestGroup",
|
|
+ "member: cn=test,ou=people,dc=example,dc=com",
|
|
+ "member: cn=t\\;,ou=people,dc=example,dc=com",
|
|
+ "member: cn=t\\*,ou=people,dc=example,dc=com");
|
|
+ result = conn.processOperation(addGroupTest);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addGroupTest2 = new AddRequest(
|
|
+ "dn: cn=Test\\<Group*2,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: groupOfNames",
|
|
+ "cn: Test<Group*2",
|
|
+ "member: cn=t\\=,ou=people,dc=example,dc=com");
|
|
+ result = conn.processOperation(addGroupTest2);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addGroupTest3 = new AddRequest(
|
|
+ "dn: cn=Test\\>Group*3,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: groupOfNames",
|
|
+ "cn: Test>Group*3",
|
|
+ "member: cn=Test\\<Group*2,ou=people,dc=example,dc=com");
|
|
+ result = conn.processOperation(addGroupTest3);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addPeopleSub = new AddRequest(
|
|
+ "dn: ou=s\\;ub,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: organizationalUnit");
|
|
+ result = conn.processOperation(addPeopleSub);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addUserTestSub = new AddRequest(
|
|
+ "dn: cn=testsub,ou=s\\;ub,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: person",
|
|
+ "objectClass: organizationalPerson",
|
|
+ "cn: testsub",
|
|
+ "sn: Testsub",
|
|
+ "userPassword: test");
|
|
+ result = conn.processOperation(addUserTestSub);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+
|
|
+ AddRequest addGroupTest4 = new AddRequest(
|
|
+ "dn: cn=TestGroup4,ou=s\\;ub,ou=people,dc=example,dc=com",
|
|
+ "objectClass: top",
|
|
+ "objectClass: groupOfNames",
|
|
+ "cn: TestGroup4",
|
|
+ "member: cn=testsub,ou=s\\;ub,ou=people,dc=example,dc=com");
|
|
+ result = conn.processOperation(addGroupTest4);
|
|
+ Assert.assertEquals(ResultCode.SUCCESS, result.getResultCode());
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ @AfterClass
|
|
+ public static void destroyLDAP() {
|
|
+ ldapServer.shutDown(true);
|
|
+ }
|
|
+}
|
|
Index: apache-tomcat-9.0.43-src/webapps/docs/changelog.xml
|
|
===================================================================
|
|
--- apache-tomcat-9.0.43-src.orig/webapps/docs/changelog.xml
|
|
+++ apache-tomcat-9.0.43-src/webapps/docs/changelog.xml
|
|
@@ -110,6 +110,10 @@
|
|
<bug>65106</bug>: Fix the ConfigFileLoader handling of file URIs when
|
|
running under a security manager on some JREs. (markt)
|
|
</fix>
|
|
+ <scode>
|
|
+ Expand coverage of unit tests for JNDIRealm using the UnboundID LDAP SDK
|
|
+ for Java. (markt)
|
|
+ </scode>
|
|
</changelog>
|
|
</subsection>
|
|
<subsection name="Coyote">
|
|
@@ -576,6 +580,11 @@
|
|
<a href="https://bugs.openjdk.java.net/browse/JDK-8255854">JRE bug</a>.
|
|
(markt)
|
|
</fix>
|
|
+ <fix>
|
|
+ Fix JNDIRealm pooling problems retrying on another bad connection. Any
|
|
+ retries are made on a new connection, just like with the single
|
|
+ connection scenario. (remm)
|
|
+ </fix>
|
|
</changelog>
|
|
</subsection>
|
|
</section>
|