Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6525c99

Browse files
authoredApr 27, 2020
Merge pull request #184 from mhindery/error-view-customization
Customized error handling improved
2 parents fc89cef + eac1019 commit 6525c99

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
@@ -4,18 +4,9 @@
44
# produce an output suitable for end user in case of SAML failure.
55
#
66

7-
from django.core.exceptions import PermissionDenied
87
from django.shortcuts import render
98

109

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

270270
try:
271271
response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries)
272-
except (StatusError, ToEarly):
272+
except (StatusError, ToEarly) as e:
273273
logger.exception("Error processing SAML Assertion.")
274-
return fail_acs_response(request)
275-
except ResponseLifetimeExceed:
274+
return fail_acs_response(request, exception=e)
275+
except ResponseLifetimeExceed as e:
276276
logger.info("SAML Assertion is no longer valid. Possibly caused by network delay or replay attack.", exc_info=True)
277-
return fail_acs_response(request)
278-
except SignatureError:
277+
return fail_acs_response(request, exception=e)
278+
except SignatureError as e:
279279
logger.info("Invalid or malformed SAML Assertion.", exc_info=True)
280-
return fail_acs_response(request)
281-
except StatusAuthnFailed:
280+
return fail_acs_response(request, exception=e)
281+
except StatusAuthnFailed as e:
282282
logger.info("Authentication denied for user by IdP.", exc_info=True)
283-
return fail_acs_response(request)
284-
except StatusRequestDenied:
283+
return fail_acs_response(request, exception=e)
284+
except StatusRequestDenied as e:
285285
logger.warning("Authentication interrupted at IdP.", exc_info=True)
286-
return fail_acs_response(request)
287-
except StatusNoAuthnContext:
286+
return fail_acs_response(request, exception=e)
287+
except StatusNoAuthnContext as e:
288288
logger.warning("Missing Authentication Context from IdP.", exc_info=True)
289-
return fail_acs_response(request)
290-
except MissingKey:
289+
return fail_acs_response(request, exception=e)
290+
except MissingKey as e:
291291
logger.exception("SAML Identity Provider is not configured correctly: certificate key is missing!")
292-
return fail_acs_response(request)
293-
except UnsolicitedResponse:
292+
return fail_acs_response(request, exception=e)
293+
except UnsolicitedResponse as e:
294294
logger.exception("Received SAMLResponse when no request has been made.")
295-
return fail_acs_response(request)
295+
return fail_acs_response(request, exception=e)
296296

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

301301
session_id = response.session_id()
302302
oq_cache.delete(session_id)
@@ -316,7 +316,7 @@ def assertion_consumer_service(request,
316316
create_unknown_user=create_unknown_user)
317317
if user is None:
318318
logger.warning("Could not authenticate user received in SAML Assertion. Session info: %s", session_info)
319-
raise PermissionDenied
319+
return fail_acs_response(request, exception=PermissionDenied('No user could be authenticated.'))
320320

321321
auth.login(request, user)
322322
_set_subject_id(request.session, session_info['name_id'])

0 commit comments

Comments
 (0)
Please sign in to comment.