Skip to content

Commit

Permalink
Add optional SMART_SELECTS_CHECK_MODEL_PERMISSION setting.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cveilleux committed Dec 2, 2020
1 parent 3892895 commit a5e13b0
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 0 deletions.
5 changes: 5 additions & 0 deletions docs/settings.md
Expand Up @@ -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.
11 changes: 11 additions & 0 deletions smart_selects/views.py
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit a5e13b0

Please sign in to comment.