@@ -228,6 +228,15 @@ def _server_port_default(self):
228
228
This can be useful in an heterogeneous environment, when supplying a UNIX username to authenticate against AD.
229
229
""" ,
230
230
)
231
+ secondary_uri = Unicode (
232
+ config = True ,
233
+ default = "" ,
234
+ help = """
235
+ Comma separated address:port of the LDAP server which can be tried to contact when
236
+ primary LDAP server is unavailable.
237
+
238
+ """ ,
239
+ )
231
240
232
241
def resolve_username (self , username_supplied_by_user ):
233
242
search_dn = self .lookup_dn_search_user
@@ -305,8 +314,31 @@ def resolve_username(self, username_supplied_by_user):
305
314
return (user_dn , response [0 ]["dn" ])
306
315
307
316
def get_connection (self , userdn , password ):
317
+ try :
318
+ return self ._get_real_connection (
319
+ userdn , password , self .server_address , self .server_port
320
+ )
321
+ except (
322
+ ldap3 .core .exceptions .LDAPSocketOpenError ,
323
+ ldap3 .core .exceptions .LDAPBindError ,
324
+ ldap3 .core .exceptions .LDAPSocketReceiveError ,
325
+ ):
326
+ for server , port in self ._get_secondary_servers ():
327
+ try :
328
+ return self ._get_real_connection (userdn , password , server , port )
329
+ except (
330
+ ldap3 .core .exceptions .LDAPSocketOpenError ,
331
+ ldap3 .core .exceptions .LDAPBindError ,
332
+ ldap3 .core .exceptions .LDAPSocketReceiveError ,
333
+ ):
334
+ continue
335
+ else :
336
+ # re-raise the last caught error
337
+ raise
338
+
339
+ def _get_real_connection (self , userdn , password , server_address , server_port ):
308
340
server = ldap3 .Server (
309
- self . server_address , port = self . server_port , use_ssl = self .use_ssl
341
+ server_address , port = server_port , use_ssl = self .use_ssl
310
342
)
311
343
auto_bind = (
312
344
ldap3 .AUTO_BIND_NO_TLS if self .use_ssl else ldap3 .AUTO_BIND_TLS_BEFORE_BIND
@@ -316,6 +348,24 @@ def get_connection(self, userdn, password):
316
348
)
317
349
return conn
318
350
351
+ def _get_secondary_servers (self ):
352
+ uri_list = self .secondary_uri .split ("," )
353
+ for uri in uri_list :
354
+ server_port = uri .strip ().split (":" )
355
+ assert len (server_port ) <= 2
356
+ if len (server_port ) == 2 :
357
+ try :
358
+ port = int (server_port [1 ])
359
+ except ValueError :
360
+ self .log .warning (
361
+ "Invalid port in secondary uri %s, use default" % uri
362
+ )
363
+ port = self ._server_port_default ()
364
+ else :
365
+ port = self ._server_port_default ()
366
+
367
+ yield (server_port [0 ], port )
368
+
319
369
def get_user_attributes (self , conn , userdn ):
320
370
attrs = {}
321
371
if self .auth_state_attributes :
0 commit comments