Skip to content

Commit 7c12aac

Browse files
authored
Merge pull request #136 from atdsaa/custom_error_handler
Custom error handler
2 parents 1474cf7 + 70e47fe commit 7c12aac

File tree

9 files changed

+73
-13
lines changed

9 files changed

+73
-13
lines changed

CHANGELOG.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
Changelog
22
=========
33

4+
`1.6.0`_ - 2021-01-25
5+
---------------------
6+
7+
**Features**
8+
9+
* New parameter called `CUSTOM_FAILED_RESPONSE_VIEW`, allowing you to set a custom django function view to handle login
10+
failures. #136
11+
412

513
`1.5.0`_ - 2021-01-18
614
---------------------
@@ -219,6 +227,7 @@ Changelog
219227

220228
* Initial release
221229

230+
.. _1.6.0: https://github.com/snok/django-auth-adfs/compare/1.5.0...1.6.0
222231
.. _1.5.0: https://github.com/jobec/django-auth-adfs/compare/1.4.1...1.5.0
223232
.. _1.4.1: https://github.com/jobec/django-auth-adfs/compare/1.4.0...1.4.1
224233
.. _1.4.0: https://github.com/jobec/django-auth-adfs/compare/1.3.1...1.4.0

django_auth_adfs/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
Adding imports here will break setup.py
55
"""
66

7-
__version__ = '1.5.0'
7+
__version__ = '1.6.0'

django_auth_adfs/config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
from django.contrib.auth import get_user_model
1515
from django.core.exceptions import ImproperlyConfigured
1616
from django.http import QueryDict
17+
from django.shortcuts import render
1718
from django.utils.module_loading import import_string
1819

1920
try:
2021
from django.urls import reverse
2122
except ImportError: # Django < 1.10
2223
from django.core.urlresolvers import reverse
2324

25+
2426
logger = logging.getLogger("django_auth_adfs")
2527

2628
AZURE_AD_SERVER = "login.microsoftonline.com"
@@ -69,6 +71,9 @@ def __init__(self):
6971
self.TIMEOUT = 5
7072
self.USERNAME_CLAIM = "winaccountname"
7173
self.JWT_LEEWAY = 0
74+
self.CUSTOM_FAILED_RESPONSE_VIEW = lambda request, error_message, status: render(
75+
request, 'django_auth_adfs/login_failed.html', {'error_message': error_message}, status=status
76+
)
7277

7378
required_settings = [
7479
"AUDIENCE",
@@ -151,6 +156,10 @@ def __init__(self):
151156
msg = "django_auth_adfs setting '{0}' has not been set".format(setting)
152157
raise ImproperlyConfigured(msg)
153158

159+
# Setup dynamic settings
160+
if not callable(self.CUSTOM_FAILED_RESPONSE_VIEW):
161+
self.CUSTOM_FAILED_RESPONSE_VIEW = import_string(self.CUSTOM_FAILED_RESPONSE_VIEW)
162+
154163
# Validate setting conflicts
155164
usermodel = get_user_model()
156165
if usermodel.USERNAME_FIELD in self.CLAIM_MAPPING.keys():

django_auth_adfs/views.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
from django.conf import settings as django_settings
55
from django.contrib.auth import authenticate, login, logout
6-
from django.shortcuts import redirect, render
6+
from django.shortcuts import redirect
77
from django.utils.http import is_safe_url
88
from django.views.generic import View
99

10-
from django_auth_adfs.config import provider_config
10+
from django_auth_adfs.config import provider_config, settings
1111
from django_auth_adfs.exceptions import MFARequired
1212

1313
logger = logging.getLogger("django_auth_adfs")
@@ -25,9 +25,11 @@ def get(self, request):
2525
code = request.GET.get("code")
2626
if not code:
2727
# Return an error message
28-
return render(request, 'django_auth_adfs/login_failed.html', {
29-
'error_message': "No authorization code was provided.",
30-
}, status=400)
28+
return settings.CUSTOM_FAILED_RESPONSE_VIEW(
29+
request,
30+
error_message="No authorization code was provided.",
31+
status=400
32+
)
3133

3234
redirect_to = request.GET.get("state")
3335
try:
@@ -54,14 +56,18 @@ def get(self, request):
5456
return redirect(redirect_to)
5557
else:
5658
# Return a 'disabled account' error message
57-
return render(request, 'django_auth_adfs/login_failed.html', {
58-
'error_message': "Your account is disabled.",
59-
}, status=403)
59+
return settings.CUSTOM_FAILED_RESPONSE_VIEW(
60+
request,
61+
error_message="Your account is disabled.",
62+
status=403
63+
)
6064
else:
6165
# Return an 'invalid login' error message
62-
return render(request, 'django_auth_adfs/login_failed.html', {
63-
'error_message': "Login failed.",
64-
}, status=401)
66+
return settings.CUSTOM_FAILED_RESPONSE_VIEW(
67+
request,
68+
error_message="Login failed.",
69+
status=401
70+
)
6571

6672

6773
class OAuth2LoginView(View):

docs/install.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ In your project's ``settings.py`` add these settings.
8383
'django_auth_adfs.middleware.LoginRequiredMiddleware',
8484
)
8585
86+
# You can point login failures to a custom Django function based view for customization of the UI
87+
CUSTOM_FAILED_RESPONSE_VIEW = 'dot.path.to.custom.views.login_failed'
88+
8689
In your project's ``urls.py`` add these paths:
8790
8891
.. code-block:: python

docs/settings_ref.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,24 @@ Allows you to set a leeway of the JWT token. See the official
180180
`PyJWT <https://pyjwt.readthedocs.io/en/stable/usage.html>`__ docs for more information.
181181

182182

183+
CUSTOM_FAILED_RESPONSE_VIEW
184+
--------------------------------
185+
* **Default**: ``lambda``
186+
* **Type**: ``str`` or ``callable``
187+
188+
Allows you to set a custom django function view to handle login failures. Can be a dot path to your
189+
Django function based view function or a callable.
190+
191+
Callable must have the following method signature accepting ``error_message`` and ``status`` arguments:
192+
193+
.. code-block:: python
194+
195+
def failed_response(request, error_message, status):
196+
# Return an error message
197+
return render(request, 'myapp/login_failed.html', {
198+
'error_message': error_message,
199+
}, status=status)
200+
183201
184202
GROUP_CLAIM
185203
-----------

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = 'django-auth-adfs'
3-
version = '1.5.0' # Remember to also change __init__.py version
3+
version = '1.6.0' # Remember to also change __init__.py version
44
description = 'A Django authentication backend for Microsoft ADFS and AzureAD'
55
authors = ['Joris Beckers <[email protected]>']
66
maintainers = ['Jonas Krüger Svensson <[email protected]>', 'Sondre Lillebø Gundersen <[email protected]>']

tests/test_settings.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ def test_required_setting(self):
4141
with patch("django_auth_adfs.config.django_settings", settings):
4242
self.assertRaises(ImproperlyConfigured, Settings)
4343

44+
def test_default_failed_response_setting(self):
45+
settings = deepcopy(django_settings)
46+
with patch("django_auth_adfs.config.django_settings", settings):
47+
s = Settings()
48+
self.assertTrue(callable(s.CUSTOM_FAILED_RESPONSE_VIEW))
49+
50+
def test_dotted_path_failed_response_setting(self):
51+
settings = deepcopy(django_settings)
52+
settings.AUTH_ADFS["CUSTOM_FAILED_RESPONSE_VIEW"] = 'tests.views.test_failed_response'
53+
with patch("django_auth_adfs.config.django_settings", settings):
54+
s = Settings()
55+
self.assertTrue(callable(s.CUSTOM_FAILED_RESPONSE_VIEW))
56+
4457

4558
class CustomSettingsTests(SimpleTestCase):
4659
def setUp(self):

tests/views.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test_failed_response(request, error_message, status):
2+
pass

0 commit comments

Comments
 (0)