diff --git a/readthedocs/api/v3/permissions.py b/readthedocs/api/v3/permissions.py index fb86f1db49a..7ce4be0a61d 100644 --- a/readthedocs/api/v3/permissions.py +++ b/readthedocs/api/v3/permissions.py @@ -1,32 +1,37 @@ -from rest_framework.permissions import IsAuthenticated +from rest_framework.permissions import BasePermission -class PublicDetailPrivateListing(IsAuthenticated): +class PublicDetailPrivateListing(BasePermission): """ Permission class for our custom use case. * Always give permission for a ``detail`` request * Only give permission for ``listing`` request if user is admin of the project + """ + + def has_permission(self, request, view): + if view.detail: + return True + + project = view._get_parent_project() + if view.has_admin_permission(request.user, project): + return True + + +class ListCreateProject(BasePermission): + + """ + Permission class to grant projects listing and project creation. + * Allow access to ``/projects`` (user's projects listing) """ def has_permission(self, request, view): - is_authenticated = super().has_permission(request, view) - if is_authenticated: - if view.basename == 'projects' and any([ - view.action == 'list', - view.action == 'create', # used to create Form in BrowsableAPIRenderer - view.action is None, # needed for BrowsableAPIRenderer - ]): - # hitting ``/projects/``, allowing - return True - - if view.detail: - return True - - project = view._get_parent_project() - if view.has_admin_permission(request.user, project): - return True - - return False + if view.basename == 'projects' and any([ + view.action == 'list', + view.action == 'create', # used to create Form in BrowsableAPIRenderer + view.action is None, # needed for BrowsableAPIRenderer + ]): + # hitting ``/projects/``, allowing + return True diff --git a/readthedocs/api/v3/views.py b/readthedocs/api/v3/views.py index 6f0d77448c3..1c4da3e2d97 100644 --- a/readthedocs/api/v3/views.py +++ b/readthedocs/api/v3/views.py @@ -11,6 +11,7 @@ UpdateModelMixin, ) from rest_framework.pagination import LimitOffsetPagination +from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import BrowsableAPIRenderer from rest_framework.response import Response from rest_framework.throttling import AnonRateThrottle, UserRateThrottle @@ -25,7 +26,7 @@ from .filters import BuildFilter, ProjectFilter, VersionFilter from .mixins import ProjectQuerySetMixin -from .permissions import PublicDetailPrivateListing +from .permissions import PublicDetailPrivateListing, ListCreateProject from .renderers import AlphabeticalSortedJSONRenderer from .serializers import ( BuildCreateSerializer, @@ -54,7 +55,7 @@ class APIv3Settings: # Using only ``TokenAuthentication`` for now, so we can give access to # specific carefully selected users only authentication_classes = (TokenAuthentication,) - permission_classes = (PublicDetailPrivateListing,) + permission_classes = (IsAuthenticated & (ListCreateProject | PublicDetailPrivateListing),) pagination_class = LimitOffsetPagination LimitOffsetPagination.default_limit = 10