Skip to content

Commit eac1019

Browse files
committed
Customized error handling improved
1 parent 46f9b83 commit eac1019

File tree

3 files changed

+39
-30
lines changed

3 files changed

+39
-30
lines changed

README.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,24 @@ setting::
315315
SAML_CONFIG_LOADER = 'python.path.to.your.callable'
316316

317317

318+
Custom error handler
319+
....................
320+
321+
When an error occurs during the authentication flow, djangosaml2 will render
322+
a simple error page with an error message and status code. You can customize
323+
this behaviour by specifying the path to your own error handler in the settings:
324+
325+
SAML_ACS_FAILURE_RESPONSE_FUNCTION = 'python.path.to.your.view'
326+
327+
This should be a view which takes a request, optional exception which occured
328+
and status code, and returns a response to serve the user. E.g. The default
329+
implementation looks like this::
330+
331+
def template_failure(request, exception=None, **kwargs):
332+
""" Renders a simple template with an error message. """
333+
return render(request, 'djangosaml2/login_error.html', {'exception': exception}, status=kwargs.get('status', 403))
334+
335+
318336
User attributes
319337
---------------
320338

djangosaml2/acs_failures.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,9 @@
55
#
66
from __future__ import unicode_literals
77

8-
from django.core.exceptions import PermissionDenied
98
from django.shortcuts import render
109

1110

12-
def template_failure(request, status=403, **kwargs):
13-
""" Renders a SAML-specific template with general authentication error description. """
14-
return render(request, 'djangosaml2/login_error.html', status=status)
15-
16-
17-
def exception_failure(request, exc_class=PermissionDenied, **kwargs):
18-
""" Rather than using a custom SAML specific template that is rendered on failure,
19-
this makes use of a standard exception handling machinery present in Django
20-
and thus ends up rendering a project-wide error page for Permission Denied exceptions.
21-
"""
22-
raise exc_class
11+
def template_failure(request, exception=None, status=403, **kwargs):
12+
""" Renders a simple template with an error message. """
13+
return render(request, 'djangosaml2/login_error.html', {'exception': exception}, status=status)

djangosaml2/views.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -293,34 +293,34 @@ def assertion_consumer_service(request,
293293

294294
try:
295295
response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries)
296-
except (StatusError, ToEarly):
296+
except (StatusError, ToEarly) as e:
297297
logger.exception("Error processing SAML Assertion.")
298-
return fail_acs_response(request)
299-
except ResponseLifetimeExceed:
298+
return fail_acs_response(request, exception=e)
299+
except ResponseLifetimeExceed as e:
300300
logger.info("SAML Assertion is no longer valid. Possibly caused by network delay or replay attack.", exc_info=True)
301-
return fail_acs_response(request)
302-
except SignatureError:
301+
return fail_acs_response(request, exception=e)
302+
except SignatureError as e:
303303
logger.info("Invalid or malformed SAML Assertion.", exc_info=True)
304-
return fail_acs_response(request)
305-
except StatusAuthnFailed:
304+
return fail_acs_response(request, exception=e)
305+
except StatusAuthnFailed as e:
306306
logger.info("Authentication denied for user by IdP.", exc_info=True)
307-
return fail_acs_response(request)
308-
except StatusRequestDenied:
307+
return fail_acs_response(request, exception=e)
308+
except StatusRequestDenied as e:
309309
logger.warning("Authentication interrupted at IdP.", exc_info=True)
310-
return fail_acs_response(request)
311-
except StatusNoAuthnContext:
310+
return fail_acs_response(request, exception=e)
311+
except StatusNoAuthnContext as e:
312312
logger.warning("Missing Authentication Context from IdP.", exc_info=True)
313-
return fail_acs_response(request)
314-
except MissingKey:
313+
return fail_acs_response(request, exception=e)
314+
except MissingKey as e:
315315
logger.exception("SAML Identity Provider is not configured correctly: certificate key is missing!")
316-
return fail_acs_response(request)
317-
except UnsolicitedResponse:
316+
return fail_acs_response(request, exception=e)
317+
except UnsolicitedResponse as e:
318318
logger.exception("Received SAMLResponse when no request has been made.")
319-
return fail_acs_response(request)
319+
return fail_acs_response(request, exception=e)
320320

321321
if response is None:
322322
logger.warning("Invalid SAML Assertion received (unknown error).")
323-
return fail_acs_response(request, status=400, exc_class=SuspiciousOperation)
323+
return fail_acs_response(request, status=400, exception=SuspiciousOperation('Unknown SAML2 error'))
324324

325325
session_id = response.session_id()
326326
oq_cache.delete(session_id)
@@ -340,7 +340,7 @@ def assertion_consumer_service(request,
340340
create_unknown_user=create_unknown_user)
341341
if user is None:
342342
logger.warning("Could not authenticate user received in SAML Assertion. Session info: %s", session_info)
343-
raise PermissionDenied
343+
return fail_acs_response(request, exception=PermissionDenied('No user could be authenticated.'))
344344

345345
auth.login(request, user)
346346
_set_subject_id(request.session, session_info['name_id'])

0 commit comments

Comments
 (0)