diff --git a/dandiapi/api/views/asset.py b/dandiapi/api/views/asset.py index 34d35e8f1..26c09a9dc 100644 --- a/dandiapi/api/views/asset.py +++ b/dandiapi/api/views/asset.py @@ -36,6 +36,7 @@ from rest_framework.exceptions import NotAuthenticated, NotFound, PermissionDenied from rest_framework.generics import get_object_or_404 from rest_framework.response import Response +from rest_framework.throttling import AnonRateThrottle, BaseThrottle from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet from rest_framework_extensions.mixins import DetailSerializerMixin, NestedViewSetMixin @@ -82,6 +83,13 @@ class AssetViewSet(DetailSerializerMixin, GenericViewSet): filter_backends = [filters.DjangoFilterBackend] filterset_class = AssetFilter + def get_throttles(self) -> list[BaseThrottle]: + # Throttle the asset list endpoint for unauthenticated users to prevent it from + # slowing down the rest of the system. + if self.action == 'list': + return [*self.throttle_classes, AnonRateThrottle] + return super().get_throttles() + def raise_if_unauthorized(self): # We need to check the dandiset to see if it's embargoed, and if so whether or not the # user has ownership diff --git a/dandiapi/settings.py b/dandiapi/settings.py index 0ba65368f..0db248abd 100644 --- a/dandiapi/settings.py +++ b/dandiapi/settings.py @@ -181,6 +181,11 @@ def mutate_configuration(configuration: type[ComposedConfiguration]): # We're configuring sentry by hand since we need to pass custom options (traces_sampler). configuration.INSTALLED_APPS.remove('composed_configuration.sentry.apps.SentryConfig') + # We only want rate-limiting enabled in production + configuration.REST_FRAMEWORK['DEFAULT_THROTTLE_RATES'] = { + 'anon': '60/minute', + } + ENABLE_GITHUB_OAUTH = True # All login attempts in production should go straight to GitHub