Skip to content

Is there a way to restrict access to DRF interface while still being able to access api-endpoints?

I’m creating a site that has Django Rest Framework as its backend and ReactJS as its frontend. I have multiple api-endpoints for various data, i.e.

/api/
/api/users/
/api/objects/
/api/other_objects/
etc

I don’t want regular users to have direct access to the DRF interface. If anyone decides to go to one of those urls, I’d prefer to redirect them to the index page if they don’t have staff status.

I tried to redirect users using decorators in views:

from django.contrib.auth.decorators import user_passes_test

def staff_check(user):
    if user.is_authenticated and user.is_staff:
        return True
    else:
        return False

@user_passes_test(staff_check, login_url='/', redirect_field_name=None)
@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'users': reverse('user-list', request=request, format=format),
        'objects': reverse('objects-list', request=request, format=format),
        'other_objects': reverse('other-objects-list', request=request, format=format)
    })

It works fine as far as redirection goes, but when my React app tries to fetch data from endpoints with decorators, it gets redirected as well.

I know that I can instead set permissions on the views, i.e.

class ObjectsList(generics.ListCreateAPIView):
    queryset = Object.objects.all().order_by('created')
    serializer_class = ObjectSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

However it still allows users to view the interface (with or without content, dependent on the permissions). Is there a way to keep endpoints accessable to React’s fetch command without redirection, but still redirect users, when they try to view the interface?

Answer

Just change your renderer class and remove default BrowsableAPIRenderer from list.

Globally, like this:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    )
}

But in your case, you could control it per view using decorator (when the interface should or not show up):

@api_view(['GET'])
@renderer_classes((JSONRenderer, BrowsableAPIRenderer))
def your_view(request, format=None):
   ...