Skip to content

Commit 1eeb9b7

Browse files
committed
fix(api): move access check to queryset
This makes the error codes match other API endpoints here.
1 parent 47f3a9f commit 1eeb9b7

3 files changed

Lines changed: 56 additions & 2 deletions

File tree

docs/changes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Weblate 2026.6
1212

1313
.. rubric:: Bug fixes
1414

15+
* Hardened :http:post:`/api/screenshots/` access checks against private project enumeration.
1516
* Searching for strings with content changes without a recorded author now supports ``changed_by:""``.
1617

1718
.. rubric:: Compatibility

weblate/screenshots/tests.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,56 @@ def test_view(self) -> None:
193193
self.assertEqual(response.status_code, 200)
194194
self.assertEqual(response["content-type"], "image/png")
195195

196+
def test_private_screenshot_actions_hidden(self) -> None:
197+
self.make_manager()
198+
self.do_upload()
199+
screenshot = Screenshot.objects.get()
200+
source = self.component.source_translation.unit_set.all()[0]
201+
202+
self.project.access_control = Project.ACCESS_PRIVATE
203+
self.project.save()
204+
self.user.groups.remove(Group.objects.get(name="Managers"))
205+
self.project.remove_user(self.user)
206+
207+
response = self.client.get(screenshot.get_absolute_url())
208+
self.assertEqual(response.status_code, 404)
209+
210+
response = self.client.get(screenshot.get_view_url())
211+
self.assertEqual(response.status_code, 404)
212+
213+
response = self.client.post(
214+
reverse("screenshot-delete", kwargs={"pk": screenshot.pk})
215+
)
216+
self.assertEqual(response.status_code, 404)
217+
218+
response = self.client.post(
219+
reverse("screenshot-js-search", kwargs={"pk": screenshot.pk}),
220+
{"q": "hello"},
221+
)
222+
self.assertEqual(response.status_code, 404)
223+
224+
response = self.client.post(
225+
reverse("screenshot-js-add", kwargs={"pk": screenshot.pk}),
226+
{"source": source.pk},
227+
)
228+
self.assertEqual(response.status_code, 404)
229+
230+
response = self.client.get(
231+
reverse("screenshot-js-get", kwargs={"pk": screenshot.pk})
232+
)
233+
self.assertEqual(response.status_code, 404)
234+
235+
response = self.client.post(
236+
reverse("screenshot-remove-source", kwargs={"pk": screenshot.pk}),
237+
{"source": source.pk},
238+
)
239+
self.assertEqual(response.status_code, 404)
240+
241+
response = self.client.post(
242+
reverse("screenshot-js-ocr", kwargs={"pk": screenshot.pk})
243+
)
244+
self.assertEqual(response.status_code, 404)
245+
196246
def test_delete(self) -> None:
197247
self.make_manager()
198248
self.do_upload()

weblate/screenshots/views.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ class ScreenshotBaseView(DetailView):
259259
model = Screenshot
260260
request: AuthenticatedHttpRequest
261261

262+
def get_queryset(self):
263+
return Screenshot.objects.filter_access(self.request.user)
264+
262265
def get_object(self, *args, **kwargs):
263266
obj = super().get_object(*args, **kwargs)
264267
self.request.user.check_access_component(obj.translation.component)
@@ -316,7 +319,7 @@ def post(self, request: AuthenticatedHttpRequest, *args, **kwargs) -> HttpRespon
316319
@require_POST
317320
@login_required
318321
def delete_screenshot(request: AuthenticatedHttpRequest, pk):
319-
obj = get_object_or_404(Screenshot, pk=pk)
322+
obj = get_object_or_404(Screenshot.objects.filter_access(request.user), pk=pk)
320323
component = obj.translation.component
321324
if not request.user.has_perm("screenshot.delete", obj.translation):
322325
raise PermissionDenied
@@ -329,7 +332,7 @@ def delete_screenshot(request: AuthenticatedHttpRequest, pk):
329332

330333

331334
def get_screenshot(request: AuthenticatedHttpRequest, pk):
332-
obj = get_object_or_404(Screenshot, pk=pk)
335+
obj = get_object_or_404(Screenshot.objects.filter_access(request.user), pk=pk)
333336
if not request.user.has_perm("screenshot.edit", obj.translation.component):
334337
raise PermissionDenied
335338
return obj

0 commit comments

Comments
 (0)