5
5
the record and assert them to the receiving SP.
6
6
"""
7
7
8
- import satosa .micro_services .base
8
+ from satosa .micro_services .base import ResponseMicroService
9
9
from satosa .logging_util import satosa_logging
10
10
from satosa .response import Redirect
11
11
from satosa .exception import SATOSAError
17
17
18
18
from ldap3 .core .exceptions import LDAPException
19
19
20
+
20
21
logger = logging .getLogger (__name__ )
21
22
23
+
22
24
class LdapAttributeStoreError (SATOSAError ):
23
25
"""
24
26
LDAP attribute store error
25
27
"""
26
28
pass
27
29
28
- class LdapAttributeStore (satosa .micro_services .base .ResponseMicroService ):
30
+
31
+ class LdapAttributeStore (ResponseMicroService ):
29
32
"""
30
33
Use identifier provided by the backend authentication service
31
34
to lookup a person record in LDAP and obtain attributes
@@ -41,11 +44,15 @@ class LdapAttributeStore(satosa.micro_services.base.ResponseMicroService):
41
44
'ldap_url' : None ,
42
45
'on_ldap_search_result_empty' : None ,
43
46
'ordered_identifier_candidates' : None ,
44
- 'pool_size' : 10 ,
45
- 'pool_keepalive' : 10 ,
46
47
'search_base' : None ,
47
48
'search_return_attributes' : None ,
48
- 'user_id_from_attrs' : []
49
+ 'user_id_from_attrs' : [],
50
+ 'read_only' : True ,
51
+ 'version' : 3 ,
52
+ 'auto_bind' : False ,
53
+ 'client_strategy' : ldap3 .RESTARTABLE ,
54
+ 'pool_size' : 10 ,
55
+ 'pool_keepalive' : 10 ,
49
56
}
50
57
51
58
def __init__ (self , config , * args , ** kwargs ):
@@ -80,7 +87,8 @@ def __init__(self, config, *args, **kwargs):
80
87
81
88
# Initialize configuration using module defaults then update
82
89
# with configuration defaults and then per-SP overrides.
83
- sp_config = copy .deepcopy (LdapAttributeStore .config_defaults )
90
+ # sp_config = copy.deepcopy(LdapAttributeStore.config_defaults)
91
+ sp_config = copy .deepcopy (self .config_defaults )
84
92
if 'default' in self .config :
85
93
sp_config .update (self .config ['default' ])
86
94
sp_config .update (config [sp ])
@@ -247,27 +255,35 @@ def _ldap_connection_factory(self, config):
247
255
if not bind_password :
248
256
raise LdapAttributeStoreError ("bind_password is not configured" )
249
257
250
- pool_size = config ['pool_size' ]
251
- pool_keepalive = config ['pool_keepalive' ]
252
-
253
258
server = ldap3 .Server (config ['ldap_url' ])
254
259
255
260
satosa_logging (logger , logging .DEBUG , "Creating a new LDAP connection" , None )
256
261
satosa_logging (logger , logging .DEBUG , "Using LDAP URL {}" .format (ldap_url ), None )
257
262
satosa_logging (logger , logging .DEBUG , "Using bind DN {}" .format (bind_dn ), None )
263
+
264
+ pool_size = config ['pool_size' ]
265
+ pool_keepalive = config ['pool_keepalive' ]
258
266
satosa_logging (logger , logging .DEBUG , "Using pool size {}" .format (pool_size ), None )
259
267
satosa_logging (logger , logging .DEBUG , "Using pool keep alive {}" .format (pool_keepalive ), None )
260
268
269
+ auto_bind = config ['auto_bind' ]
270
+ client_strategy = config ['client_strategy' ]
271
+ read_only = config ['read_only' ]
272
+ version = config ['version' ]
273
+
261
274
try :
262
275
connection = ldap3 .Connection (
263
276
server ,
264
277
bind_dn ,
265
278
bind_password ,
266
- auto_bind = True ,
267
- client_strategy = ldap3 .REUSABLE ,
279
+ auto_bind = auto_bind ,
280
+ client_strategy = client_strategy ,
281
+ read_only = read_only ,
282
+ version = version ,
268
283
pool_size = pool_size ,
269
284
pool_keepalive = pool_keepalive
270
- )
285
+ )
286
+ satosa_logging (logger , logging .DEBUG , "Successfully connected to LDAP server" , None )
271
287
272
288
except LDAPException as e :
273
289
msg = "Caught exception when connecting to LDAP server: {}" .format (e )
@@ -400,46 +416,63 @@ def process(self, context, data):
400
416
# Initialize an empty LDAP record. The first LDAP record found using the ordered
401
417
# list of search filter values will be the record used.
402
418
record = None
403
- err_msg = None
419
+ results = None
420
+ exp_msg = None
404
421
405
- try :
422
+ for filter_val in filter_values :
406
423
connection = config ['connection' ]
407
- for filter_val in filter_values :
408
- if record :
409
- break
410
-
411
- search_filter = '({0}={1})' .format (config ['ldap_identifier_attribute' ], filter_val )
412
- satosa_logging (logger , logging .DEBUG , "Constructed search filter {}" .format (search_filter ), context .state )
413
-
414
- satosa_logging (logger , logging .DEBUG , "Querying LDAP server..." , context .state )
415
- message_id = connection .search (config ['search_base' ], search_filter , attributes = config ['search_return_attributes' ].keys ())
416
- responses = connection .get_response (message_id )[0 ]
417
- satosa_logging (logger , logging .DEBUG , "Done querying LDAP server" , context .state )
418
- satosa_logging (logger , logging .DEBUG , "LDAP server returned {} records" .format (len (responses )), context .state )
419
-
420
- # for now consider only the first record found (if any)
421
- if len (responses ) > 0 :
422
- if len (responses ) > 1 :
423
- satosa_logging (logger , logging .WARN , "LDAP server returned {} records using search filter value {}" .format (len (responses ), filter_val ), context .state )
424
- record = responses [0 ]
425
- break
426
- except LDAPException as e :
427
- err_msg = "Caught LDAP exception: {}" .format (e )
428
- except LdapAttributeStoreError as e :
429
- err_msg = "Caught LDAP Attribute Store exception: {}" .format (e )
430
- except Exception as e :
431
- err_msg = "Caught unhandled exception: {}" .format (e )
432
-
433
- if err_msg :
434
- satosa_logging (logger , logging .ERROR , err_msg , context .state )
435
- return super ().process (context , data )
424
+ search_filter = '({0}={1})' .format (config ['ldap_identifier_attribute' ], filter_val )
425
+ # show ldap filter
426
+ satosa_logging (logger , logging .INFO , "LDAP query for {}" .format (search_filter ), context .state )
427
+ satosa_logging (logger , logging .DEBUG , "Constructed search filter {}" .format (search_filter ), context .state )
428
+
429
+ try :
430
+ # message_id only works in REUSABLE async connection strategy
431
+ results = connection .search (config ['search_base' ], search_filter , attributes = config ['search_return_attributes' ].keys ())
432
+ except LDAPException as err :
433
+ exp_msg = "Caught LDAP exception: {}" .format (err )
434
+ except LdapAttributeStoreError as err :
435
+ exp_msg = "Caught LDAP Attribute Store exception: {}" .format (err )
436
+ except Exception as err :
437
+ exp_msg = "Caught unhandled exception: {}" .format (err )
438
+
439
+ if exp_msg :
440
+ satosa_logging (logger , logging .ERROR , exp_msg , context .state )
441
+ return super ().process (context , data )
442
+
443
+ if not results :
444
+ satosa_logging (logger , logging .DEBUG , "Querying LDAP server: No results for {}." .format (filter_val ), context .state )
445
+ continue
446
+
447
+ if isinstance (results , bool ):
448
+ responses = connection .entries
449
+ else :
450
+ responses = connection .get_response (results )[0 ]
451
+
452
+ satosa_logging (logger , logging .DEBUG , "Done querying LDAP server" , context .state )
453
+ satosa_logging (logger , logging .INFO , "LDAP server returned {} records" .format (len (responses )), context .state )
454
+
455
+ # for now consider only the first record found (if any)
456
+ if len (responses ) > 0 :
457
+ if len (responses ) > 1 :
458
+ satosa_logging (logger , logging .WARN , "LDAP server returned {} records using search filter value {}" .format (len (responses ), filter_val ), context .state )
459
+ record = responses [0 ]
460
+ break
436
461
437
462
# Before using a found record, if any, to populate attributes
438
463
# clear any attributes incoming to this microservice if so configured.
439
464
if config ['clear_input_attributes' ]:
440
465
satosa_logging (logger , logging .DEBUG , "Clearing values for these input attributes: {}" .format (data .attributes ), context .state )
441
466
data .attributes = {}
442
467
468
+ # this adapts records with different search and connection strategy (sync without pool), it should be tested with anonimous bind with message_id
469
+ if isinstance (results , bool ):
470
+ drec = dict ()
471
+ drec ['dn' ] = record .entry_dn if hasattr (record , 'entry_dn' ) else ''
472
+ drec ['attributes' ] = record .entry_attributes_as_dict if hasattr (record , 'entry_attributes_as_dict' ) else {}
473
+ record = drec
474
+ # ends adaptation
475
+
443
476
# Use a found record, if any, to populate attributes and input for NameID
444
477
if record :
445
478
satosa_logging (logger , logging .DEBUG , "Using record with DN {}" .format (record ["dn" ]), context .state )
@@ -466,4 +499,4 @@ def process(self, context, data):
466
499
return Redirect (url )
467
500
468
501
satosa_logging (logger , logging .DEBUG , "Returning data.attributes {}" .format (str (data .attributes )), context .state )
469
- return super () .process (context , data )
502
+ return ResponseMicroService .process (self , context , data )
0 commit comments