Fixes #5470: Fix exception when making OPTIONS request for a REST API list endpoint

This commit is contained in:
Jeremy Stretch 2020-12-15 21:04:47 -05:00
parent e2d17b1999
commit c835ec5102
4 changed files with 55 additions and 1 deletions

View File

@ -12,6 +12,7 @@
* [#5463](https://github.com/netbox-community/netbox/issues/5463) - Back-to-back Circuit Termination throws AttributeError exception
* [#5465](https://github.com/netbox-community/netbox/issues/5465) - Correct return URL when disconnecting a cable from a device
* [#5466](https://github.com/netbox-community/netbox/issues/5466) - Fix validation for required custom fields
* [#5470](https://github.com/netbox-community/netbox/issues/5470) - Fix exception when making `OPTIONS` request for a REST API list endpoint
---

View File

@ -1,10 +1,45 @@
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.utils.encoding import force_str
from rest_framework import exceptions
from rest_framework.metadata import SimpleMetadata
from rest_framework.request import clone_request
from netbox.api import ContentTypeField
class ContentTypeMetadata(SimpleMetadata):
class BulkOperationMetadata(SimpleMetadata):
def determine_actions(self, request, view):
"""
Replace the stock determine_actions() method to assess object permissions only
when viewing a specific object. This is necessary to support OPTIONS requests
with bulk update in place (see #5470).
"""
actions = {}
for method in {'PUT', 'POST'} & set(view.allowed_methods):
view.request = clone_request(request, method)
try:
# Test global permissions
if hasattr(view, 'check_permissions'):
view.check_permissions(view.request)
# Test object permissions (if viewing a specific object)
if method == 'PUT' and view.lookup_url_kwarg and hasattr(view, 'get_object'):
view.get_object()
except (exceptions.APIException, PermissionDenied, Http404):
pass
else:
# If user has appropriate permissions for the view, include
# appropriate metadata about the fields that should be supplied.
serializer = view.get_serializer()
actions[method] = self.get_serializer_info(serializer)
finally:
view.request = request
return actions
class ContentTypeMetadata(BulkOperationMetadata):
def get_field_info(self, field):
field_info = super().get_field_info(field)

View File

@ -467,6 +467,7 @@ REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
'DEFAULT_METADATA_CLASS': 'netbox.api.metadata.BulkOperationMetadata',
'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.OptionalLimitOffsetPagination',
'DEFAULT_PERMISSION_CLASSES': (
'netbox.api.authentication.TokenPermissions',

View File

@ -109,6 +109,15 @@ class APIViewTestCases:
url = self._get_detail_url(instance2)
self.assertHttpStatus(self.client.get(url, **self.header), status.HTTP_404_NOT_FOUND)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_options_object(self):
"""
Make an OPTIONS request for a single object.
"""
url = self._get_detail_url(self._get_queryset().first())
response = self.client.options(url, **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
class ListObjectsViewTestCase(APITestCase):
brief_fields = []
@ -174,6 +183,14 @@ class APIViewTestCases:
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 2)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_options_objects(self):
"""
Make an OPTIONS request for a list endpoint.
"""
response = self.client.options(self._get_list_url(), **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
class CreateObjectViewTestCase(APITestCase):
create_data = []
validation_excluded_fields = []