|
15 | 15 |
|
16 | 16 | import java.io.IOException; |
17 | 17 | import java.nio.charset.StandardCharsets; |
| 18 | +import java.util.Arrays; |
18 | 19 | import java.util.Hashtable; |
| 20 | +import java.util.Iterator; |
19 | 21 | import java.util.Properties; |
20 | 22 |
|
21 | 23 | import javax.naming.Context; |
@@ -100,11 +102,19 @@ public class LdapAuthenticationHandler implements AuthenticationHandler { |
100 | 102 | */ |
101 | 103 | public static final String ENABLE_START_TLS = TYPE + ".enablestarttls"; |
102 | 104 |
|
| 105 | + /** |
| 106 | + * Constant for the number of attempts per LDAP server URL. |
| 107 | + */ |
| 108 | + public static final String NUM_ATTEMPTS = TYPE + ".numattempts"; |
| 109 | + |
| 110 | + public static final String NUM_ATTEMPTS_DEFAULT = "2"; |
| 111 | + |
103 | 112 | private String ldapDomain; |
104 | 113 | private String baseDN; |
105 | | - private String providerUrl; |
106 | 114 | private Boolean enableStartTls; |
107 | 115 | private Boolean disableHostNameVerification; |
| 116 | + private Iterator<String> ldapUrls; |
| 117 | + private int numAttempts; |
108 | 118 |
|
109 | 119 | /** |
110 | 120 | * Configure StartTLS LDAP extension for this handler. |
@@ -138,25 +148,31 @@ public String getType() { |
138 | 148 | @Override |
139 | 149 | public void init(Properties config) throws ServletException { |
140 | 150 | this.baseDN = config.getProperty(BASE_DN); |
141 | | - this.providerUrl = config.getProperty(PROVIDER_URL); |
| 151 | + String providerUrl = config.getProperty(PROVIDER_URL); |
| 152 | + if (providerUrl == null) { |
| 153 | + throw new NullPointerException("The LDAP URI can not be null"); |
| 154 | + } |
| 155 | + this.ldapUrls = Arrays.asList(providerUrl.split(",")).iterator(); |
142 | 156 | this.ldapDomain = config.getProperty(LDAP_BIND_DOMAIN); |
143 | 157 | this.enableStartTls = |
144 | 158 | Boolean.valueOf(config.getProperty(ENABLE_START_TLS, "false")); |
| 159 | + this.numAttempts = |
| 160 | + Integer.parseInt(config.getProperty(NUM_ATTEMPTS, NUM_ATTEMPTS_DEFAULT)); |
145 | 161 |
|
146 | | - if (this.providerUrl == null) { |
147 | | - throw new NullPointerException("The LDAP URI can not be null"); |
148 | | - } |
149 | 162 | if (!((this.baseDN == null) |
150 | 163 | ^ (this.ldapDomain == null))) { |
151 | 164 | throw new IllegalArgumentException( |
152 | 165 | "Either LDAP base DN or LDAP domain value needs to be specified"); |
153 | 166 | } |
154 | 167 | if (this.enableStartTls) { |
155 | | - String tmp = this.providerUrl.toLowerCase(); |
156 | | - if (tmp.startsWith("ldaps")) { |
157 | | - throw new IllegalArgumentException( |
158 | | - "Can not use ldaps and StartTLS option at the same time"); |
| 168 | + while (ldapUrls.hasNext()) { |
| 169 | + String tmp = ldapUrls.next().toLowerCase(); |
| 170 | + if (tmp.startsWith("ldaps")) { |
| 171 | + throw new IllegalArgumentException( |
| 172 | + "Can not use ldaps and StartTLS option at the same time"); |
| 173 | + } |
159 | 174 | } |
| 175 | + |
160 | 176 | } |
161 | 177 | } |
162 | 178 |
|
@@ -234,22 +250,33 @@ private AuthenticationToken authenticateUser(String userName, |
234 | 250 | bindDN = "uid=" + userName + "," + baseDN; |
235 | 251 | } |
236 | 252 |
|
237 | | - if (this.enableStartTls) { |
238 | | - authenticateWithTlsExtension(bindDN, password); |
239 | | - } else { |
240 | | - authenticateWithoutTlsExtension(bindDN, password); |
| 253 | + while (ldapUrls.hasNext()){ |
| 254 | + String currentLdapUrl = ldapUrls.next(); |
| 255 | + for (int attempt = 1; attempt <= numAttempts; attempt++) { |
| 256 | + try { |
| 257 | + if (this.enableStartTls) { |
| 258 | + authenticateWithTlsExtension(bindDN, password, currentLdapUrl); |
| 259 | + } else { |
| 260 | + authenticateWithoutTlsExtension(bindDN, password, currentLdapUrl); |
| 261 | + } |
| 262 | + return new AuthenticationToken(userName, userName, TYPE); |
| 263 | + } catch (AuthenticationException e) { |
| 264 | + logger.warn("Failed to authenticate for user {} (attempt={}/{}) using {}. " + |
| 265 | + "Exception: ", bindDN, attempt, numAttempts, currentLdapUrl, e); |
| 266 | + } |
| 267 | + } |
241 | 268 | } |
242 | 269 |
|
243 | | - return new AuthenticationToken(userName, userName, TYPE); |
| 270 | + return null; |
244 | 271 | } |
245 | 272 |
|
246 | | - private void authenticateWithTlsExtension(String userDN, String password) |
247 | | - throws AuthenticationException { |
| 273 | + private void authenticateWithTlsExtension(String userDN, String password, |
| 274 | + String ldapUrl) throws AuthenticationException { |
248 | 275 | LdapContext ctx = null; |
249 | 276 | Hashtable<String, Object> env = new Hashtable<String, Object>(); |
250 | 277 | env.put(Context.INITIAL_CONTEXT_FACTORY, |
251 | 278 | "com.sun.jndi.ldap.LdapCtxFactory"); |
252 | | - env.put(Context.PROVIDER_URL, providerUrl); |
| 279 | + env.put(Context.PROVIDER_URL, ldapUrl); |
253 | 280 |
|
254 | 281 | try { |
255 | 282 | // Create initial context |
@@ -290,12 +317,12 @@ public boolean verify(String hostname, SSLSession session) { |
290 | 317 | } |
291 | 318 | } |
292 | 319 |
|
293 | | - private void authenticateWithoutTlsExtension(String userDN, String password) |
294 | | - throws AuthenticationException { |
| 320 | + private void authenticateWithoutTlsExtension(String userDN, String password, |
| 321 | + String ldapUrl) throws AuthenticationException { |
295 | 322 | Hashtable<String, Object> env = new Hashtable<String, Object>(); |
296 | 323 | env.put(Context.INITIAL_CONTEXT_FACTORY, |
297 | 324 | "com.sun.jndi.ldap.LdapCtxFactory"); |
298 | | - env.put(Context.PROVIDER_URL, providerUrl); |
| 325 | + env.put(Context.PROVIDER_URL, ldapUrl); |
299 | 326 | env.put(Context.SECURITY_AUTHENTICATION, SECURITY_AUTHENTICATION); |
300 | 327 | env.put(Context.SECURITY_PRINCIPAL, userDN); |
301 | 328 | env.put(Context.SECURITY_CREDENTIALS, password); |
|
0 commit comments