3232from django .views .decorators .http import require_POST
3333from django .shortcuts import render
3434from django .template import TemplateDoesNotExist
35- from django .utils .six import text_type , binary_type , PY3
35+
36+ try :
37+ from django .utils .six import text_type , binary_type , PY3
38+ except ImportError :
39+ import sys
40+ PY3 = sys .version_info [0 ] == 3
41+ text_type = str
42+ binary_type = bytes
43+
3644from django .views .decorators .csrf import csrf_exempt
3745
3846from saml2 import BINDING_HTTP_REDIRECT , BINDING_HTTP_POST
4957
5058from djangosaml2 .cache import IdentityCache , OutstandingQueriesCache
5159from djangosaml2 .cache import StateCache
60+ from djangosaml2 .exceptions import IdPConfigurationMissing
5261from djangosaml2 .conf import get_config
5362from djangosaml2 .overrides import Saml2Client
5463from djangosaml2 .signals import post_authenticated
@@ -136,6 +145,13 @@ def login(request,
136145 selected_idp = request .GET .get ('idp' , None )
137146 conf = get_config (config_loader_path , request )
138147
148+ kwargs = {}
149+ # pysaml needs a string otherwise: "cannot serialize True (type bool)"
150+ if getattr (conf , '_sp_force_authn' ):
151+ kwargs ['force_authn' ] = "true"
152+ if getattr (conf , '_sp_allow_create' , "false" ):
153+ kwargs ['allow_create' ] = "true"
154+
139155 # is a embedded wayf needed?
140156 idps = available_idps (conf )
141157 if selected_idp is None and len (idps ) > 1 :
@@ -144,7 +160,13 @@ def login(request,
144160 'available_idps' : idps .items (),
145161 'came_from' : came_from ,
146162 })
147-
163+ else :
164+ # is the first one, otherwise next logger message will print None
165+ if not idps :
166+ raise IdPConfigurationMissing (('IdP configuration is missing or '
167+ 'its metadata is expired.' ))
168+ selected_idp = list (idps .keys ())[0 ]
169+
148170 # choose a binding to try first
149171 sign_requests = getattr (conf , '_sp_authn_requests_signed' , False )
150172 binding = BINDING_HTTP_POST if sign_requests else BINDING_HTTP_REDIRECT
@@ -184,7 +206,7 @@ def login(request,
184206 session_id , result = client .prepare_for_authenticate (
185207 entityid = selected_idp , relay_state = came_from ,
186208 binding = binding , sign = False , sigalg = sigalg ,
187- nsprefix = nsprefix )
209+ nsprefix = nsprefix , ** kwargs )
188210 except TypeError as e :
189211 logger .error ('Unable to know which IdP to use' )
190212 return HttpResponse (text_type (e ))
@@ -200,10 +222,11 @@ def login(request,
200222 return HttpResponse (text_type (e ))
201223 session_id , request_xml = client .create_authn_request (
202224 location ,
203- binding = binding )
225+ binding = binding ,
226+ ** kwargs )
204227 try :
205228 if PY3 :
206- saml_request = base64 .b64encode (binary_type (request_xml , 'UTF-8' ))
229+ saml_request = base64 .b64encode (binary_type (request_xml , 'UTF-8' )). decode ( 'utf-8' )
207230 else :
208231 saml_request = base64 .b64encode (binary_type (request_xml ))
209232
@@ -458,7 +481,17 @@ def do_logout_service(request, data, binding, config_loader_path=None, next_page
458481 relay_state = data .get ('RelayState' , '' ))
459482 state .sync ()
460483 auth .logout (request )
461- return HttpResponseRedirect (get_location (http_info ))
484+ if (
485+ http_info .get ('method' , 'GET' ) == 'POST' and
486+ 'data' in http_info and
487+ ('Content-type' , 'text/html' ) in http_info .get ('headers' , [])
488+ ):
489+ # need to send back to the IDP a signed POST response with user session
490+ # return HTML form content to browser with auto form validation
491+ # to finally send request to the IDP
492+ return HttpResponse (http_info ['data' ])
493+ else :
494+ return HttpResponseRedirect (get_location (http_info ))
462495 else :
463496 logger .error ('No SAMLResponse or SAMLRequest parameter found' )
464497 raise Http404 ('No SAMLResponse or SAMLRequest parameter found' )
0 commit comments