Skip to content

Commit 46b85eb

Browse files
committed
fix: djangosaml2.views refactor
- code cleanup and linting - signature and digest generalization - some f-strings - http-redirect sso binding cleanup
1 parent 4eec525 commit 46b85eb

File tree

1 file changed

+55
-41
lines changed

1 file changed

+55
-41
lines changed

djangosaml2/views.py

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import base64
1717
import logging
18+
import saml2
1819

1920
from django.conf import settings
2021
from django.contrib import auth
@@ -31,7 +32,6 @@
3132
from django.views.decorators.csrf import csrf_exempt
3233
from django.views.generic import View
3334
from django.utils.module_loading import import_string
34-
from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
3535
from saml2.client_base import LogoutError
3636
from saml2.config import SPConfig
3737
from saml2.ident import code, decode
@@ -191,61 +191,73 @@ def get(self, request, *args, **kwargs):
191191
selected_idp = list(configured_idps.keys())[0]
192192

193193
# choose a binding to try first
194-
sign_requests = getattr(conf, '_sp_authn_requests_signed', False)
195-
binding = BINDING_HTTP_POST if sign_requests else BINDING_HTTP_REDIRECT
196-
logger.debug('Trying binding %s for IDP %s', binding, selected_idp)
194+
binding = getattr(settings, 'SAML_DEFAULT_BINDING', saml2.BINDING_HTTP_POST)
195+
logger.debug(f'Trying binding {binding} for IDP {selected_idp}')
197196

198197
# ensure our selected binding is supported by the IDP
199198
supported_bindings = get_idp_sso_supported_bindings(
200199
selected_idp, config=conf)
200+
201201
if binding not in supported_bindings:
202-
logger.debug('Binding %s not in IDP %s supported bindings: %s',
203-
binding, selected_idp, supported_bindings)
204-
if binding == BINDING_HTTP_POST:
205-
logger.warning('IDP %s does not support %s, trying %s',
206-
selected_idp, binding, BINDING_HTTP_REDIRECT)
207-
binding = BINDING_HTTP_REDIRECT
202+
logger.debug(
203+
f'Binding {binding} not in IDP {selected_idp} '
204+
f'supported bindings: {supported_bindings}. Trying to switch ...',
205+
)
206+
if binding == saml2.BINDING_HTTP_POST:
207+
logger.warning(
208+
f'IDP {selected_idp} does not support {binding} '
209+
f'trying {saml2.BINDING_HTTP_REDIRECT}',
210+
)
211+
binding = saml2.BINDING_HTTP_REDIRECT
208212
else:
209-
logger.warning('IDP %s does not support %s, trying %s',
210-
selected_idp, binding, BINDING_HTTP_POST)
211-
binding = BINDING_HTTP_POST
213+
logger.warning(
214+
f'IDP {selected_idp} does not support {binding} '
215+
f'trying {saml2.BINDING_HTTP_POST}',
216+
)
217+
binding = saml2.BINDING_HTTP_POST
212218
# if switched binding still not supported, give up
213219
if binding not in supported_bindings:
214220
raise UnsupportedBinding(
215-
'IDP %s does not support %s or %s', selected_idp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT)
221+
f'IDP {selected_idp} does not support '
222+
f'{saml2.BINDING_HTTP_POST} and {saml2.BINDING_HTTP_REDIRECT}'
223+
)
216224

217225
client = Saml2Client(conf)
218226
http_response = None
219227

220-
kwargs = {}
228+
# SSO options
229+
sign_requests = getattr(conf, '_sp_authn_requests_signed', False)
230+
sso_kwargs = {}
231+
if sign_requests:
232+
sso_kwargs["sigalg"] = settings.SAML_CONFIG['service']['sp']\
233+
.get('signing_algorithm',
234+
saml2.xmldsig.SIG_RSA_SHA256)
235+
sso_kwargs["digest_alg"] = settings.SAML_CONFIG['service']['sp']\
236+
.get('digest_algorithm',
237+
saml2.xmldsig.DIGEST_SHA256)
238+
221239
# pysaml needs a string otherwise: "cannot serialize True (type bool)"
222240
if getattr(conf, '_sp_force_authn', False):
223-
kwargs['force_authn'] = "true"
241+
sso_kwargs['force_authn'] = "true"
224242
if getattr(conf, '_sp_allow_create', False):
225-
kwargs['allow_create'] = "true"
243+
sso_kwargs['allow_create'] = "true"
226244

227-
logger.debug('Redirecting user to the IdP via %s binding.', binding)
228-
if binding == BINDING_HTTP_REDIRECT:
245+
# custom nsprefixes
246+
sso_kwargs['nsprefix'] = get_namespace_prefixes()
247+
248+
logger.debug(f'Redirecting user to the IdP via {binding} binding.')
249+
if binding == saml2.BINDING_HTTP_REDIRECT:
229250
try:
230-
nsprefix = get_namespace_prefixes()
231-
if sign_requests:
232-
# do not sign the xml itself, instead use the sigalg to
233-
# generate the signature as a URL param
234-
sig_alg_option_map = {
235-
'sha1': SIG_RSA_SHA1, 'sha256': SIG_RSA_SHA256}
236-
sig_alg_option = getattr(
237-
conf, '_sp_authn_requests_signed_alg', 'sha1')
238-
kwargs["sigalg"] = sig_alg_option_map[sig_alg_option]
239251
session_id, result = client.prepare_for_authenticate(
240252
entityid=selected_idp, relay_state=next_path,
241-
binding=binding, sign=sign_requests, nsprefix=nsprefix,
242-
**kwargs)
253+
binding=binding, sign=sign_requests,
254+
**sso_kwargs)
243255
except TypeError as e:
244256
logger.error('Unable to know which IdP to use')
245257
return HttpResponse(str(e))
246258
else:
247259
http_response = HttpResponseRedirect(get_location(result))
248-
elif binding == BINDING_HTTP_POST:
260+
elif binding == saml2.BINDING_HTTP_POST:
249261
if self.post_binding_form_template:
250262
# get request XML to build our own html based on the template
251263
try:
@@ -256,7 +268,7 @@ def get(self, request, *args, **kwargs):
256268
session_id, request_xml = client.create_authn_request(
257269
location,
258270
binding=binding,
259-
**kwargs)
271+
**sso_kwargs)
260272
try:
261273
if isinstance(request_xml, AuthnRequest):
262274
# request_xml will be an instance of AuthnRequest if the message is not signed
@@ -271,8 +283,8 @@ def get(self, request, *args, **kwargs):
271283
'RelayState': next_path,
272284
},
273285
})
274-
except TemplateDoesNotExist:
275-
pass
286+
except TemplateDoesNotExist as e:
287+
logger.error(f'TemplateDoesNotExist: {e}')
276288

277289
if not http_response:
278290
# use the html provided by pysaml2 if no template was specified or it didn't exist
@@ -286,13 +298,15 @@ def get(self, request, *args, **kwargs):
286298
else:
287299
http_response = HttpResponse(result['data'])
288300
else:
289-
raise UnsupportedBinding('Unsupported binding: %s', binding)
301+
raise UnsupportedBinding(f'Unsupported binding: {binding}')
290302

291303
# success, so save the session ID and return our response
292304
oq_cache = OutstandingQueriesCache(request.saml_session)
293305
oq_cache.set(session_id, next_path)
294306
logger.debug(
295-
'Saving the session_id "%s" in the OutstandingQueries cache', oq_cache.__dict__)
307+
f'Saving the session_id "{oq_cache.__dict__}" '
308+
'in the OutstandingQueries cache',
309+
)
296310
return http_response
297311

298312

@@ -344,7 +358,7 @@ def post(self, request, attribute_mapping=None, create_unknown_user=None):
344358
_exception = None
345359
try:
346360
response = client.parse_authn_request_response(request.POST['SAMLResponse'],
347-
BINDING_HTTP_POST,
361+
saml2.BINDING_HTTP_POST,
348362
outstanding_queries)
349363
except (StatusError, ToEarly) as e:
350364
_exception = e
@@ -525,12 +539,12 @@ def get(self, request, *args, **kwargs):
525539
for entityid, logout_info in result.items():
526540
if isinstance(logout_info, tuple):
527541
binding, http_info = logout_info
528-
if binding == BINDING_HTTP_POST:
542+
if binding == saml2.BINDING_HTTP_POST:
529543
logger.debug(
530544
'Returning form to the IdP to continue the logout process')
531545
body = ''.join(http_info['data'])
532546
return HttpResponse(body)
533-
elif binding == BINDING_HTTP_REDIRECT:
547+
elif binding == saml2.BINDING_HTTP_REDIRECT:
534548
logger.debug(
535549
'Redirecting to the IdP to continue the logout process')
536550
return HttpResponseRedirect(get_location(http_info))
@@ -569,10 +583,10 @@ class LogoutView(SPConfigMixin, View):
569583
logout_error_template = 'djangosaml2/logout_error.html'
570584

571585
def get(self, request, *args, **kwargs):
572-
return self.do_logout_service(request, request.GET, BINDING_HTTP_REDIRECT, *args, **kwargs)
586+
return self.do_logout_service(request, request.GET, saml2.BINDING_HTTP_REDIRECT, *args, **kwargs)
573587

574588
def post(self, request, *args, **kwargs):
575-
return self.do_logout_service(request, request.POST, BINDING_HTTP_POST, *args, **kwargs)
589+
return self.do_logout_service(request, request.POST, saml2.BINDING_HTTP_POST, *args, **kwargs)
576590

577591
def do_logout_service(self, request, data, binding):
578592
logger.debug('Logout service started')

0 commit comments

Comments
 (0)