24
24
import platform
25
25
import shelve
26
26
import traceback
27
- from urlparse import parse_qs
27
+ from urlparse import parse_qs , urlparse
28
28
29
- from paste .httpexceptions import HTTPSeeOther
29
+ from paste .httpexceptions import HTTPSeeOther , HTTPRedirection
30
30
from paste .httpexceptions import HTTPNotImplemented
31
31
from paste .httpexceptions import HTTPInternalServerError
32
32
from paste .request import parse_dict_querystring
@@ -133,6 +133,7 @@ def __init__(self, rememberer_name, config, saml_client, wayf, cache,
133
133
self .cache = cache
134
134
self .discosrv = discovery
135
135
self .idp_query_param = idp_query_param
136
+ self .logout_endpoints = [urlparse (ep )[2 ] for ep in config .endpoint ("single_logout_service" )]
136
137
137
138
try :
138
139
self .metadata = self .conf .metadata
@@ -282,10 +283,22 @@ def challenge(self, environ, _status, _app_headers, _forget_headers):
282
283
283
284
_cli = self .saml_client
284
285
285
- # this challenge consist in logging out
286
- if 'rwpc.logout' in environ :
287
- # ignore right now?
288
- pass
286
+
287
+ if 'REMOTE_USER' in environ :
288
+ name_id = decode (environ ["REMOTE_USER" ])
289
+
290
+ _cli = self .saml_client
291
+ path_info = environ ['PATH_INFO' ]
292
+
293
+ if 'samlsp.logout' in environ :
294
+ responses = _cli .global_logout (name_id )
295
+ return self ._handle_logout (responses )
296
+
297
+ if 'samlsp.pending' in environ :
298
+ response = environ ['samlsp.pending' ]
299
+ if isinstance (response , HTTPRedirection ):
300
+ response .headers += _forget_headers
301
+ return response
289
302
290
303
#logger = environ.get('repoze.who.logger','')
291
304
@@ -405,7 +418,8 @@ def identify(self, environ):
405
418
"""
406
419
#logger = environ.get('repoze.who.logger', '')
407
420
408
- if "CONTENT_LENGTH" not in environ or not environ ["CONTENT_LENGTH" ]:
421
+ query = parse_dict_querystring (environ )
422
+ if ("CONTENT_LENGTH" not in environ or not environ ["CONTENT_LENGTH" ]) and "SAMLResponse" not in query and "SAMLRequest" not in query :
409
423
logger .debug ('[identify] get or empty post' )
410
424
return {}
411
425
@@ -420,7 +434,7 @@ def identify(self, environ):
420
434
query = parse_dict_querystring (environ )
421
435
logger .debug ('[sp.identify] query: %s' % (query ,))
422
436
423
- if "SAMLResponse" in query :
437
+ if "SAMLResponse" in query or "SAMLRequest" in query :
424
438
post = query
425
439
binding = BINDING_HTTP_REDIRECT
426
440
else :
@@ -433,7 +447,21 @@ def identify(self, environ):
433
447
pass
434
448
435
449
try :
436
- if "SAMLResponse" not in post :
450
+ path_info = environ ['PATH_INFO' ]
451
+ logout = False
452
+ if path_info in self .logout_endpoints :
453
+ logout = True
454
+
455
+ if logout and "SAMLRequest" in post :
456
+ print ("logout request received" )
457
+ try :
458
+ response = self .saml_client .handle_logout_request (post ["SAMLRequest" ], self .saml_client .users .subjects ()[0 ], binding )
459
+ environ ['samlsp.pending' ] = self ._handle_logout (response )
460
+ return {}
461
+ except :
462
+ import traceback
463
+ traceback .print_exc ()
464
+ elif "SAMLResponse" not in post :
437
465
logger .info ("[sp.identify] --- NOT SAMLResponse ---" )
438
466
# Not for me, put the post back where next in line can
439
467
# find it
@@ -444,9 +472,23 @@ def identify(self, environ):
444
472
# check for SAML2 authN response
445
473
#if self.debug:
446
474
try :
447
- session_info = self ._eval_authn_response (
448
- environ , cgi_field_storage_to_dict (post ),
449
- binding = binding )
475
+ if logout :
476
+ response = self .saml_client .parse_logout_request_response (post ["SAMLResponse" ], binding )
477
+ if response :
478
+ action = self .saml_client .handle_logout_response (response )
479
+ request = None
480
+ if type (action ) == dict :
481
+ request = self ._handle_logout (action )
482
+ else :
483
+ #logout complete
484
+ request = HTTPSeeOther (headers = [('Location' , "/" )])
485
+ if request :
486
+ environ ['samlsp.pending' ] = request
487
+ return {}
488
+ else :
489
+ session_info = self ._eval_authn_response (
490
+ environ , cgi_field_storage_to_dict (post ),
491
+ binding = binding )
450
492
except Exception , err :
451
493
environ ["s2repoze.saml_error" ] = err
452
494
return {}
@@ -528,10 +570,23 @@ def _service_url(self, environ, qstr=None):
528
570
#noinspection PyUnusedLocal
529
571
def authenticate (self , environ , identity = None ):
530
572
if identity :
573
+ tktuser = identity .get ('repoze.who.plugins.auth_tkt.userid' , None )
574
+ if tktuser and self .saml_client .is_logged_in (decode (tktuser )):
575
+ return tktuser
531
576
return identity .get ('login' , None )
532
577
else :
533
578
return None
534
579
580
+ def _handle_logout (self , responses ):
581
+ if 'data' in responses :
582
+ ht_args = responses
583
+ else :
584
+ ht_args = responses [responses .keys ()[0 ]][1 ]
585
+ if not ht_args ["data" ] and ht_args ["headers" ][0 ][0 ] == "Location" :
586
+ logger .debug ('redirect to: %s' % ht_args ["headers" ][0 ][1 ])
587
+ return HTTPSeeOther (headers = ht_args ["headers" ])
588
+ else :
589
+ return ht_args ["data" ]
535
590
536
591
def make_plugin (remember_name = None , # plugin for remember
537
592
cache = "" , # cache
0 commit comments