Skip to content

Commit aa52509

Browse files
authored
Merge pull request #1446 from TOMToolkit/1445-adding-password-reset-to-login-page
Match path prefixes in locked auth strategy
2 parents 19bbbc4 + 972082f commit aa52509

5 files changed

Lines changed: 59 additions & 13 deletions

File tree

docs/common/customsettings.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ details and available hooks.
165165
Default: []
166166

167167
With an `AUTH_STRATEGY <#auth-strategy>`__ value of **LOCKED**, urls in
168-
this list will remain visible to unauthenticated users. You might add
169-
the homepage (‘/’), for example.
168+
this list will remain visible to unauthenticated users. You can also use wild cards to open an entire path.
169+
You might add the homepage (‘/’), for example, or anything with a path that looks like '/accounts/reset/*/'.
170170
171171
`TARGET_PERMISSIONS_ONLY <#target-permissions-only>`__
172172
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tom_base/settings.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@
310310
# Define custom DataProcessor class
311311
# DATA_PROCESSOR_CLASS = 'mytom.custom_data_processor.CustomDataProcessor'
312312

313-
# Authentication strategy can either be LOCKED (required login for all views)
313+
# Authentication strategy can either be LOCKED (required login for all views, bypassed with OPEN_URLS)
314314
# or READ_ONLY (read only access to views)
315315
AUTH_STRATEGY = 'READ_ONLY'
316316

@@ -330,8 +330,8 @@
330330
"name", "type", "observations", "saved_data"
331331
]
332332

333-
# URLs that should be allowed access even with AUTH_STRATEGY = LOCKED
334-
# for example: OPEN_URLS = ['/', '/about']
333+
# URLs that should be allowed access even with AUTH_STRATEGY = LOCKED. Can use wildcards.
334+
# for example: OPEN_URLS = ['/', '/about', '/accounts/reset/*/']
335335
OPEN_URLS = []
336336

337337
HOOKS = {

tom_common/middleware.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fnmatch
12
from django.conf import settings
23
from django.contrib import messages
34
from django.http import HttpResponseForbidden
@@ -35,11 +36,13 @@ def __init__(self, get_response):
3536
self.open_urls = [reverse('login')] + getattr(settings, 'OPEN_URLS', [])
3637

3738
def __call__(self, request):
38-
if settings.AUTH_STRATEGY == 'LOCKED':
39-
if not request.user.is_authenticated and request.path_info not in self.open_urls:
40-
return HttpResponseForbidden()
41-
42-
return self.get_response(request)
39+
if settings.AUTH_STRATEGY == 'LOCKED' and not request.user.is_authenticated:
40+
for url in self.open_urls:
41+
if fnmatch.fnmatch(request.path_info, url):
42+
return self.get_response(request)
43+
return HttpResponseForbidden()
44+
else:
45+
return self.get_response(request)
4346

4447

4548
class Raise403Middleware:

tom_common/tests.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,49 @@ def test_user_can_access_view(self):
247247
self.assertContains(response, 'Create Targets')
248248

249249

250+
class TestAuthStrategyMiddleware(TestCase):
251+
login_url = '/accounts/login/'
252+
253+
@override_settings(AUTH_STRATEGY='LOCKED', OPEN_URLS=[])
254+
def test_locked_unauthenticated_request_redirects_to_login(self):
255+
# Raise403Middleware converts the 403 from AuthStrategyMiddleware to a redirect
256+
response = self.client.get(reverse('tom_targets:list'))
257+
self.assertRedirects(
258+
response, self.login_url + '?next=' + reverse('tom_targets:list'), status_code=302
259+
)
260+
261+
@override_settings(AUTH_STRATEGY='LOCKED', OPEN_URLS=['/accounts/reset/*/'])
262+
def test_locked_password_reset_wildcard_matches_uid_token(self):
263+
# /accounts/reset/abc123xyz/ should match the wildcard
264+
response = self.client.get('/accounts/reset/abc123xyz/foobarfoo/')
265+
self.assertNotEqual(response.status_code, 302)
266+
267+
@override_settings(AUTH_STRATEGY='LOCKED', OPEN_URLS=['/accounts/reset/*/'])
268+
def test_locked_password_reset_wildcard_does_not_match_unrelated_path(self):
269+
# /accounts/profile/ should not match /accounts/reset/*/
270+
response = self.client.get('/accounts/profile/')
271+
self.assertRedirects(
272+
response, self.login_url + '?next=/accounts/profile/', status_code=302
273+
)
274+
275+
@override_settings(AUTH_STRATEGY='LOCKED', OPEN_URLS=[])
276+
def test_locked_login_url_always_open(self):
277+
response = self.client.get(reverse('login'))
278+
self.assertNotEqual(response.status_code, 302)
279+
280+
@override_settings(AUTH_STRATEGY='LOCKED', OPEN_URLS=[])
281+
def test_locked_authenticated_user_allowed(self):
282+
user = User.objects.create_user(username='testuser', password='password')
283+
self.client.force_login(user)
284+
response = self.client.get(reverse('tom_targets:list'))
285+
self.assertEqual(response.status_code, 200)
286+
287+
@override_settings(AUTH_STRATEGY='READ_ONLY', OPEN_URLS=[])
288+
def test_read_only_unauthenticated_allowed(self):
289+
response = self.client.get(reverse('tom_targets:list'))
290+
self.assertEqual(response.status_code, 200)
291+
292+
250293
class CommentDeleteViewTest(TestCase):
251294
def setUp(self):
252295
self.site = Site.objects.get_current()

tom_setup/templates/tom_setup/settings.tmpl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ GENERAL_SEARCH_FUNCTIONS = {}
328328
# This list can be used to add custom target parameters to the output from the target selection tool
329329
SELECTION_EXTRA_FIELDS = []
330330

331-
# Authentication strategy can either be LOCKED (required login for all views)
331+
# Authentication strategy can either be LOCKED (required login for all views, bypassed with OPEN_URLS)
332332
# or READ_ONLY (read only access to views)
333333
AUTH_STRATEGY = 'READ_ONLY'
334334

@@ -348,8 +348,8 @@ TARGET_LIST_COLUMNS = [
348348
"name", "type", "observations", "saved_data"
349349
]
350350

351-
# URLs that should be allowed access even with AUTH_STRATEGY = LOCKED
352-
# for example: OPEN_URLS = ['/', '/about']
351+
# URLs that should be allowed access even with AUTH_STRATEGY = LOCKED. Can use wildcards.
352+
# for example: OPEN_URLS = ['/', '/about', '/accounts/reset/*/']
353353
OPEN_URLS = []
354354
355355
HOOKS = {

0 commit comments

Comments
 (0)