7
7
from django .core .exceptions import PermissionDenied
8
8
from django .db .models import BooleanField , Case , Value , When
9
9
from django .http import Http404
10
- from django .shortcuts import get_object_or_404
11
10
from django .template .loader import render_to_string
12
- from rest_framework import decorators , permissions , status , viewsets
11
+ from rest_framework import decorators , status , viewsets
13
12
from rest_framework .mixins import CreateModelMixin , UpdateModelMixin
14
13
from rest_framework .parsers import JSONParser , MultiPartParser
15
14
from rest_framework .renderers import BaseRenderer , JSONRenderer
16
15
from rest_framework .response import Response
17
16
18
- from readthedocs .api .v2 .permissions import (
19
- APIRestrictedPermission ,
20
- HasBuildAPIKey ,
21
- IsOwner ,
22
- )
17
+ from readthedocs .api .v2 .permissions import HasBuildAPIKey , IsOwner , ReadOnlyPermission
23
18
from readthedocs .api .v2 .utils import normalize_build_command
24
19
from readthedocs .builds .constants import INTERNAL
25
20
from readthedocs .builds .models import Build , BuildCommandResult , Version
@@ -129,18 +124,16 @@ class UserSelectViewSet(viewsets.ReadOnlyModelViewSet):
129
124
View set that varies serializer class based on request user credentials.
130
125
131
126
Viewsets using this class should have an attribute `admin_serializer_class`,
132
- which is a serializer that might have more fields that only admin/staff
133
- users require. If the user is staff , this class will be returned instead.
127
+ which is a serializer that might have more fields that only the builders
128
+ require. If the request is using a Build API key , this class will be returned instead.
134
129
135
130
By default read-only endpoints will be allowed,
136
131
to allow write endpoints, inherit from the proper ``rest_framework.mixins.*`` classes.
137
132
"""
138
133
139
134
def get_serializer_class (self ):
140
135
try :
141
- if (
142
- self .request .user .is_staff or self .request .build_api_key
143
- ) and self .admin_serializer_class is not None :
136
+ if self .request .build_api_key and self .admin_serializer_class is not None :
144
137
return self .admin_serializer_class
145
138
except AttributeError :
146
139
pass
@@ -169,7 +162,7 @@ class ProjectViewSet(DisableListEndpoint, UpdateModelMixin, UserSelectViewSet):
169
162
170
163
"""List, filter, etc, Projects."""
171
164
172
- permission_classes = [HasBuildAPIKey | APIRestrictedPermission ]
165
+ permission_classes = [HasBuildAPIKey | ReadOnlyPermission ]
173
166
renderer_classes = (JSONRenderer ,)
174
167
serializer_class = ProjectSerializer
175
168
admin_serializer_class = ProjectAdminSerializer
@@ -214,7 +207,7 @@ def get_queryset_for_api_key(self, api_key):
214
207
215
208
class VersionViewSet (DisableListEndpoint , UpdateModelMixin , UserSelectViewSet ):
216
209
217
- permission_classes = [HasBuildAPIKey | APIRestrictedPermission ]
210
+ permission_classes = [HasBuildAPIKey | ReadOnlyPermission ]
218
211
renderer_classes = (JSONRenderer ,)
219
212
serializer_class = VersionSerializer
220
213
admin_serializer_class = VersionAdminSerializer
@@ -232,7 +225,7 @@ def get_queryset(self):
232
225
233
226
234
227
class BuildViewSet (DisableListEndpoint , UpdateModelMixin , UserSelectViewSet ):
235
- permission_classes = [HasBuildAPIKey | APIRestrictedPermission ]
228
+ permission_classes = [HasBuildAPIKey | ReadOnlyPermission ]
236
229
renderer_classes = (JSONRenderer , PlainTextBuildRenderer )
237
230
model = Build
238
231
filterset_fields = ('project__slug' , 'commit' )
@@ -245,7 +238,7 @@ def get_serializer_class(self):
245
238
pre-process the `command` field before returning it to the user, and we
246
239
also want to have a specific serializer for admins.
247
240
"""
248
- if self .request .user . is_staff or self . request . build_api_key :
241
+ if self .request .build_api_key :
249
242
# Logic copied from `UserSelectViewSet.get_serializer_class`
250
243
# and extended to choose serializer from self.action
251
244
if self .action not in ["list" , "retrieve" ]:
@@ -255,22 +248,20 @@ def get_serializer_class(self):
255
248
256
249
@decorators .action (
257
250
detail = False ,
258
- permission_classes = [HasBuildAPIKey | permissions . IsAdminUser ],
251
+ permission_classes = [HasBuildAPIKey ],
259
252
methods = ['get' ],
260
253
)
261
254
def concurrent (self , request , ** kwargs ):
262
255
project_slug = request .GET .get ('project__slug' )
263
256
build_api_key = request .build_api_key
264
- if build_api_key :
265
- if project_slug != build_api_key .project .slug :
266
- log .info (
267
- "Project slug doesn't match the one attached to the API key." ,
268
- project_slug = project_slug ,
269
- )
270
- raise Http404 ()
271
- project = build_api_key .project
272
- else :
273
- project = get_object_or_404 (Project , slug = project_slug )
257
+ if project_slug != build_api_key .project .slug :
258
+ log .warning (
259
+ "Project slug doesn't match the one attached to the API key." ,
260
+ api_key_id = build_api_key .id ,
261
+ project_slug = project_slug ,
262
+ )
263
+ raise Http404 ()
264
+ project = build_api_key .project
274
265
limit_reached , concurrent , max_concurrent = Build .objects .concurrent (project )
275
266
data = {
276
267
'limit_reached' : limit_reached ,
@@ -319,7 +310,7 @@ def retrieve(self, *args, **kwargs):
319
310
320
311
@decorators .action (
321
312
detail = True ,
322
- permission_classes = [HasBuildAPIKey | permissions . IsAdminUser ],
313
+ permission_classes = [HasBuildAPIKey ],
323
314
methods = ['post' ],
324
315
)
325
316
def reset (self , request , ** kwargs ):
@@ -334,27 +325,25 @@ def get_queryset_for_api_key(self, api_key):
334
325
335
326
class BuildCommandViewSet (DisableListEndpoint , CreateModelMixin , UserSelectViewSet ):
336
327
parser_classes = [JSONParser , MultiPartParser ]
337
- permission_classes = [HasBuildAPIKey | APIRestrictedPermission ]
328
+ permission_classes = [HasBuildAPIKey | ReadOnlyPermission ]
338
329
renderer_classes = (JSONRenderer ,)
339
330
serializer_class = BuildCommandSerializer
340
331
model = BuildCommandResult
341
332
342
333
def perform_create (self , serializer ):
343
334
"""Restrict creation to builds attached to the project from the api key."""
344
335
build_pk = serializer .validated_data ["build" ].pk
345
- api_key = self .request .build_api_key
346
- if api_key and not api_key .project .builds .filter (pk = build_pk ).exists ():
336
+ build_api_key = self .request .build_api_key
337
+ if not build_api_key .project .builds .filter (pk = build_pk ).exists ():
347
338
raise PermissionDenied ()
348
- # If the request isn't attached to a build api key,
349
- # the user doing the request is a superuser, so it has access to all projects.
350
339
return super ().perform_create (serializer )
351
340
352
341
def get_queryset_for_api_key (self , api_key ):
353
342
return self .model .objects .filter (build__project = api_key .project )
354
343
355
344
356
345
class DomainViewSet (DisableListEndpoint , UserSelectViewSet ):
357
- permission_classes = [APIRestrictedPermission ]
346
+ permission_classes = [ReadOnlyPermission ]
358
347
renderer_classes = (JSONRenderer ,)
359
348
serializer_class = DomainSerializer
360
349
model = Domain
0 commit comments