From a5e13b03eb7dced5977216618137e275fbe9b471 Mon Sep 17 00:00:00 2001 From: Cedric Veilleux Date: Thu, 6 Aug 2020 12:21:50 -0400 Subject: [PATCH] Add optional SMART_SELECTS_CHECK_MODEL_PERMISSION setting. If enabled, a standard django permission check is performed on the chained model in the API endpoint. This prevents information leaks to unauthorized users. The extra check is disabled by default to not break backward compatibility. --- docs/settings.md | 5 +++++ smart_selects/views.py | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/settings.md b/docs/settings.md index f0b3a97..03de7ca 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -8,3 +8,8 @@ `USE_DJANGO_JQUERY` : By default, `smart_selects` loads jQuery from Google's CDN. However, it can use jQuery from Django's admin area. Set `USE_DJANGO_JQUERY = True` to enable this behaviour. + +`SMART_SELECTS_CHECK_MODEL_PERMISSION` +: By default, `smart_selects` does not check if the logged-in user has access to view the chained model permissions. + Setting this option to `True` will cause `smart-selects` to check if the user has the view_model + permission in the ajax endpoint. diff --git a/smart_selects/views.py b/smart_selects/views.py index d19e946..dd34cae 100644 --- a/smart_selects/views.py +++ b/smart_selects/views.py @@ -2,6 +2,7 @@ from django.core.exceptions import PermissionDenied from django.db.models import Q from django.http import JsonResponse +from django.conf import settings from six import iteritems from django.views.decorators.cache import never_cache @@ -76,6 +77,11 @@ def filterchain(request, app, model, field, foreign_key_app_name, foreign_key_mo for f in foreign_model_class._meta.get_fields()]): raise PermissionDenied("Smart select disallowed") + # SECURITY: If SMART_SELECTS_CHECK_MODEL_PERMISSIONS is enabled, do extra model permission check + if getattr(settings, 'SMART_SELECTS_CHECK_MODEL_PERMISSION', False): + if not request.user.has_perm('{0}.view_{1}'.format(foreign_key_app_name, foreign_key_model_name)): + raise PermissionDenied("Smart select disallowed") + # filter queryset using limit_choices_to limit_choices_to = get_limit_choices_to(foreign_key_app_name, foreign_key_model_name, foreign_key_field_name) queryset = get_queryset(model_class, manager, limit_choices_to) @@ -105,6 +111,11 @@ def filterchain_all(request, app, model, field, foreign_key_app_name, for f in foreign_model_class._meta.get_fields()]): raise PermissionDenied("Smart select disallowed") + # SECURITY: If SMART_SELECTS_CHECK_MODEL_PERMISSIONS is enabled, do extra model permission check + if getattr(settings, 'SMART_SELECTS_CHECK_MODEL_PERMISSION', False): + if not request.user.has_perm('{0}.view_{1}'.format(foreign_key_app_name, foreign_key_model_name)): + raise PermissionDenied("Smart select disallowed") + # filter queryset using limit_choices_to limit_choices_to = get_limit_choices_to(foreign_key_app_name, foreign_key_model_name, foreign_key_field_name) queryset = get_queryset(model_class, limit_choices_to=limit_choices_to)