diff --git a/AUTHORS.md b/AUTHORS.md index 7e4a93e..8e04bf0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -60,3 +60,4 @@ an issue. - [Greg Wong](https://github.com/gregorywong) - [Michael V. Battista](https://github.com/mvbattista) - [William Abbott](https://github.com/wrabit) +- [Henry Harutyunyan](https://github.com/henryh9n) (Revolut) diff --git a/README.md b/README.md index 9a54191..f30c0f8 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ python setup.py install 'ACCEPTED_TIME_DIFF': None, # Accepted time difference between your server and the Identity Provider 'ALLOWED_REDIRECT_HOSTS': ["https://myfrontendclient.com"], # Allowed hosts to redirect to using the ?next parameter 'TOKEN_REQUIRED': True, # Whether or not to require the token parameter in the SAML assertion + 'DISABLE_EXCEPTION_HANDLER': True, # Whether the custom exception handler should be used } ``` @@ -231,7 +232,7 @@ Some of the following settings are related to how this module operates. The rest Click to see the module settings | **Field name** | **Description** | **Data type(s)** | **Default value(s)** | **Example** | -| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------| | **METADATA\_AUTO\_CONF\_URL** | Auto SAML2 metadata configuration URL | `str` | `None` | `https://ORG.okta.com/app/APP-ID/sso/saml/metadata` | | **METADATA\_LOCAL\_FILE\_PATH** | SAML2 metadata configuration file path | `str` | `None` | `/path/to/the/metadata.xml` | | **KEY_FILE** | SAML2 private key file path. Required for AUTHN\_REQUESTS\_SIGNED | `str` | `None` | `/path/to/the/key.pem` | @@ -273,6 +274,7 @@ Some of the following settings are related to how this module operates. The rest | **WANT\_RESPONSE\_SIGNED** | Set this to `False` if you don't want your provider to sign the response. | `bool` | `True` | | | **ACCEPTED\_TIME\_DIFF** | Sets the [accepted time diff](https://pysaml2.readthedocs.io/en/latest/howto/config.html#accepted-time-diff) in seconds | `int` or `None` | `None` | | | **ALLOWED\_REDIRECT\_HOSTS** | Allowed hosts to redirect to using the `?next=` parameter | `list` | `[]` | `['https://app.example.com', 'https://api.exmaple.com']` | +| **DISABLE\_EXCEPTION\_HANDLER** | Set this to `True` if you want to disable the exception handler. Make sure to handle the `SAMLAuthError`s and other exceptions. | `bool` | `False` | | ### Triggers @@ -349,11 +351,21 @@ def get_custom_token_query(refresh): ``` -## Customize Error Messages +## Exception Handling +This library implements an exception handler that returns an error response with a default error template. See the +section below if you want to implement a custom error template. + +If you want to disable error handling, set `DISABLE_EXCEPTION_HANDLER` to `True`. In this case the library will raise +`SAMLAuthError` when an error happens and you might need to implement an exception handler. This might come in handy if +you are using the library for an API. + +## Customize Error Messages and Templates The default permission `denied`, `error` and user `welcome` page can be overridden. To override these pages put a template named 'django\_saml2\_auth/error.html', 'django\_saml2\_auth/welcome.html' or 'django\_saml2\_auth/denied.html' in your project's template folder. +> [!Note] +> If you set `DISABLE_EXCEPTION_HANDLER` to `True`, the custom error pages will not be displayed. If a 'django\_saml2\_auth/welcome.html' template exists, that page will be shown to the user upon login instead of the user being redirected to the previous visited page. This welcome page can contain some first-visit notes and welcome words. The [Django user object](https://docs.djangoproject.com/en/1.9/ref/contrib/auth/#django.contrib.auth.models.User) is available within the template as the `user` template variable. diff --git a/django_saml2_auth/tests/test_utils.py b/django_saml2_auth/tests/test_utils.py index c998999..f49cf0e 100644 --- a/django_saml2_auth/tests/test_utils.py +++ b/django_saml2_auth/tests/test_utils.py @@ -5,6 +5,8 @@ import pytest from django.http import HttpRequest, HttpResponse from django.urls import NoReverseMatch +from pytest_django.fixtures import SettingsWrapper + from django_saml2_auth.exceptions import SAMLAuthError from django_saml2_auth.utils import ( exception_handler, @@ -39,7 +41,7 @@ def hello(_: HttpRequest) -> HttpResponse: return HttpResponse(content="Hello, world!") -def goodbye(_: HttpRequest) -> None: +def goodbye(_: HttpRequest) -> HttpResponse: """Simple view function for testing exception_handler Args: @@ -139,6 +141,25 @@ def test_exception_handler_handle_exception(): assert "Reason: Internal world error!" in contents +def test_exception_handler_diabled_success(settings: SettingsWrapper): + """Test exception_handler decorator in disabled state with a valid function.""" + settings.SAML2_AUTH["DISABLE_EXCEPTION_HANDLER"] = True + + decorated_hello = exception_handler(hello) + result = decorated_hello(HttpRequest()) + assert result.content.decode("utf-8") == "Hello, world!" + + +def test_exception_handler_disabled_on_exception(settings: SettingsWrapper): + """Test exception_handler decorator in a disabled state to make sure it raises the + exception.""" + settings.SAML2_AUTH["DISABLE_EXCEPTION_HANDLER"] = True + + decorated_goodbye = exception_handler(goodbye) + with pytest.raises(SAMLAuthError): + decorated_goodbye(HttpRequest()) + + def test_jwt_well_formed(): """Test if passed RelayState is a well formed JWT""" token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MjQyIiwibmFtZSI6Ikplc3NpY2EgVGVtcG9yYWwiLCJuaWNrbmFtZSI6Ikplc3MifQ.EDkUUxaM439gWLsQ8a8mJWIvQtgZe0et3O3z4Fd_J8o" # noqa diff --git a/django_saml2_auth/utils.py b/django_saml2_auth/utils.py index 008134d..ae7f6fe 100644 --- a/django_saml2_auth/utils.py +++ b/django_saml2_auth/utils.py @@ -166,6 +166,9 @@ def exception_handler( Decorated view function with exception handling """ + if dictor(settings.SAML2_AUTH, "DISABLE_EXCEPTION_HANDLER", False): + return function + def handle_exception(exc: Exception, request: HttpRequest) -> HttpResponse: """Render page with exception details