diff --git a/src/documents/tests/test_api_tasks.py b/src/documents/tests/test_api_tasks.py index aee080900..ba55f0e5d 100644 --- a/src/documents/tests/test_api_tasks.py +++ b/src/documents/tests/test_api_tasks.py @@ -578,6 +578,66 @@ class TestSummary: assert response.status_code == status.HTTP_400_BAD_REQUEST assert "days" in response.data + def test_days_capped_at_365(self, admin_client: APIClient) -> None: + """?days= above 365 is silently clamped to 365 so tasks older than a year are excluded.""" + old_task = PaperlessTaskFactory(task_type=PaperlessTask.TaskType.CONSUME_FILE) + PaperlessTask.objects.filter(pk=old_task.pk).update( + date_created=timezone.now() - timedelta(days=400), + ) + + response = admin_client.get(ENDPOINT + "summary/", {"days": 10000}) + + assert response.status_code == status.HTTP_200_OK + assert len(response.data) == 0 + + +@pytest.mark.django_db() +class TestSummaryPermissions: + def test_monitoring_user_can_access_summary( + self, + user_client: APIClient, + regular_user, + ) -> None: + """A user with view_system_status but no document permissions can access summary/.""" + regular_user.user_permissions.add( + Permission.objects.get(codename="view_system_status"), + ) + + response = user_client.get(ENDPOINT + "summary/") + + assert response.status_code == status.HTTP_200_OK + + def test_monitoring_user_sees_all_tasks( + self, + user_client: APIClient, + regular_user, + admin_user, + ) -> None: + """Monitoring user sees aggregate data for all tasks, not just unowned ones.""" + regular_user.user_permissions.add( + Permission.objects.get(codename="view_system_status"), + ) + PaperlessTaskFactory( + owner=admin_user, + task_type=PaperlessTask.TaskType.CONSUME_FILE, + status=PaperlessTask.Status.SUCCESS, + ) + + response = user_client.get(ENDPOINT + "summary/") + + assert response.status_code == status.HTTP_200_OK + total = sum(item["total_count"] for item in response.data) + assert total == 1 + + def test_unauthenticated_cannot_access_summary( + self, + rest_api_client: APIClient, + ) -> None: + """Unauthenticated requests to summary/ return 401.""" + response = rest_api_client.get(ENDPOINT + "summary/") + + assert response.status_code == status.HTTP_401_UNAUTHORIZED + @pytest.mark.django_db() class TestActive: diff --git a/src/documents/views.py b/src/documents/views.py index 51b1abe96..6d08a0658 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -3946,18 +3946,28 @@ class TasksViewSet(ReadOnlyModelViewSet[PaperlessTask]): count = tasks.update(acknowledged=True) return Response({"result": count}) + def get_permissions(self): + if self.action == "summary" and has_system_status_permission( + getattr(self.request, "user", None), + ): + return [IsAuthenticated()] + return super().get_permissions() + @action(methods=["get"], detail=False) def summary(self, request): """Aggregated task statistics per task_type over the last N days (default 30).""" try: - days = max(1, int(request.query_params.get("days", 30))) + days = min(365, max(1, int(request.query_params.get("days", 30)))) except (TypeError, ValueError): return Response( {"days": "Must be a positive integer."}, status=status.HTTP_400_BAD_REQUEST, ) cutoff = timezone.now() - timedelta(days=days) - queryset = self.get_queryset().filter(date_created__gte=cutoff) + if has_system_status_permission(request.user): + queryset = PaperlessTask.objects.filter(date_created__gte=cutoff) + else: + queryset = self.get_queryset().filter(date_created__gte=cutoff) data = queryset.values("task_type").annotate( total_count=Count("id"),