How to hide or change permissions in / api / endpoint in Django Rest?
I would like to disable or set admin rights on / api / endpoint, otherwise no one will be able to view our entire API. However, I don't want to completely remove the django rest UI - I want to prevent people from viewing the list of all endpoints.
There seem to be two possible solutions:
- Insert a redirect or hide the endpoint somehow
- Set admin only permissions on endpoint (and only that endpoint - so, only in / api / - not on / api / things /)
I'm wondering if there is a "correct" way to get around this.
source to share
You can use permission classes
to set up admin rights checks on the endpoint /api/
.
Lets say MyView
is a presentation class for an endpoint /api/
. In this view, we will set the permission class IsAdminUser
. This will make our API available to admin users only.
For classes:
from rest_framework.permissions import IsAdminUser
class MyView(..):
permisssion_classes = (IsAdminUser,) # set the permission class
..
For functional views:
@permission_classes((IsAdminUser, ))
def my_view(..):
...
This will deny any user that matters user.is_staff
how False
else the permission will be allowed.
source to share
My approach to this problem may not be the most elegant, but it serves as an example.
urls.py
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet, base_name='users')
router.register(r'groups', views.GroupViewSet, base_name='groups')
router.register(r'tasks', views.TasksViewSet, base_name='tasks')
urlpatterns = [
url(r'^$', views.APIRoot.as_view()),
url(r'', include(router.urls)),
...
]
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class APIRoot(APIView):
"""
API Root ...
"""
def get(self, request):
data = {
"users": "http://localhost:5000/users/",
"groups": "http://localhost:5000/groups/",
"tasks": "http://localhost:5000/tasks/",
}
if not request.user.is_superuser:
data.pop("users")
data.pop("groups")
return Response(data)
source to share
Rahul's answer is technically correct, but I needed to do something else first.
I originally had:
router.register(r'things', things_viewsets.ThingViewSet)
...
urlpatterns = patterns('',
url((r'^api/'), include(router.urls)),
....
)
What I needed to do:
router.register(r'api/things', things_viewsets.ThingViewSet)
....
urlpatterns += router.urls
Then I was able to just remove / api / and have it 404, or I could add auth to it as Rahul explained.
source to share
You should use SimpleRouter()
instead DefaultRouter()
in yoururls.py
https://www.django-rest-framework.org/api-guide/routers/#simplerouter
source to share
The class APIRootView
that defines the view for the root /api/
endpoint has an undocumented private attribute _ignore_model_permissions
set to True
. This attribute is used to ensure that it DjangoModelPermissions
does not apply to the root view because the root view DefaultRouter
does not have an attribute queryset
that is required to work DjangoModelPermissions
.
In short, if you set APIRootView
permissions APIRootView
(for example IsAuthenticated
), then it is safe to set to a _ignore_model_permissions
value False
, which will cause the permission class to be applied to the root view. As an example:
class MyRouter(routers.DefaultRouter):
def __init__(self, *args, **kwargs):
self.APIRootView._ignore_model_permissions = False
self.APIRootView.permission_classes = [IsAuthenticated]
super().__init__(*args, **kwargs)
Please note that we modify self.APIRootView
because DefaultRouter
APIRootView
in an attribute .
Remember, this _ignore_model_permissions
is a private undocumented attribute and therefore there is no guarantee that it will continue to work in the future.
source to share