From 9d3cd17b402d8cabb0ffa6fe56558188bc5e98ba Mon Sep 17 00:00:00 2001 From: Sanjin <102841251+duckduckgrayduck@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:05:29 +0000 Subject: [PATCH 1/2] Update org views --- .gitignore | 2 ++ .../organizations/tests/test_views.py | 31 ++++++++++++++----- documentcloud/organizations/views.py | 8 ++--- documentcloud/users/tests/test_views.py | 2 +- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 3104360d..b1d474b4 100644 --- a/.gitignore +++ b/.gitignore @@ -304,3 +304,5 @@ scratch/ CLAUDE.md .claude + +rootCA.pem diff --git a/documentcloud/organizations/tests/test_views.py b/documentcloud/organizations/tests/test_views.py index f574c45d..a1477de2 100644 --- a/documentcloud/organizations/tests/test_views.py +++ b/documentcloud/organizations/tests/test_views.py @@ -19,17 +19,20 @@ @pytest.mark.django_db() class TestOrganizationAPI: - def test_list(self, client): + def test_list(self, client, user): """List organizations""" + client.force_authenticate(user=user) size = 10 - OrganizationFactory.create_batch(size) + OrganizationFactory.create_batch(size, individual=False) response = client.get("/api/organizations/") assert response.status_code == status.HTTP_200_OK response_json = json.loads(response.content) - assert len(response_json["results"]) == size + # +1 for the user's individual organization created by the user fixture + assert len(response_json["results"]) == size + 1 - def test_list_id_in_filter(self, client): + def test_list_id_in_filter(self, client, user): """List organizations""" + client.force_authenticate(user=user) size = 10 orgs = OrganizationFactory.create_batch(size) some_ids = [str(o.id) for o in orgs[:5]] @@ -38,8 +41,9 @@ def test_list_id_in_filter(self, client): response_json = json.loads(response.content) assert len(response_json["results"]) == len(some_ids) - def test_list_filter(self, client): + def test_list_filter(self, client, user): """List organizations""" + client.force_authenticate(user=user) names = ["abcdef", "ABC123", "abcxyz", "xyz123", "x12345", "qwerty"] for name in names: OrganizationFactory.create(name=name) @@ -51,20 +55,33 @@ def test_list_filter(self, client): response_json = json.loads(response.content) assert len(response_json["results"]) == size - def test_retrieve(self, client, organization): + def test_retrieve(self, client, user, organization): """Test retrieving an organization""" + client.force_authenticate(user=user) response = client.get(f"/api/organizations/{organization.pk}/") assert response.status_code == status.HTTP_200_OK response_json = json.loads(response.content) serializer = OrganizationSerializer(organization) assert response_json == serializer.data - def test_retrieve_bad(self, client): + def test_retrieve_bad(self, client, user): """Cannot view a private organization""" + client.force_authenticate(user=user) organization = OrganizationFactory(private=True) response = client.get(f"/api/organizations/{organization.pk}/") assert response.status_code == status.HTTP_404_NOT_FOUND + def test_list_unauthenticated(self, client): + """Unauthenticated users cannot list organizations""" + OrganizationFactory.create_batch(3) + response = client.get("/api/organizations/") + assert response.status_code == status.HTTP_403_FORBIDDEN + + def test_retrieve_unauthenticated(self, client, organization): + """Unauthenticated users cannot retrieve an organization""" + response = client.get(f"/api/organizations/{organization.pk}/") + assert response.status_code == status.HTTP_403_FORBIDDEN + def test_ai_credits(self, client, pro_organization, user): """Test charging AI credits""" response = client.post( diff --git a/documentcloud/organizations/views.py b/documentcloud/organizations/views.py index 285cafc9..68f339db 100644 --- a/documentcloud/organizations/views.py +++ b/documentcloud/organizations/views.py @@ -2,7 +2,7 @@ from django.db.models.expressions import F, Value from rest_framework import status, viewsets from rest_framework.decorators import action -from rest_framework.permissions import IsAuthenticated +from rest_framework.permissions import DjangoObjectPermissions from rest_framework.response import Response # Third Party @@ -12,8 +12,7 @@ # DocumentCloud from documentcloud.addons.models import AddOnRun from documentcloud.core.permissions import ( - DjangoObjectPermissionsOrAnonReadOnly, - OrganizationAICreditsPermissions, + OrganizationAICreditsPermissions ) from documentcloud.organizations.exceptions import InsufficientAICreditsError from documentcloud.organizations.models import Organization @@ -25,9 +24,8 @@ class OrganizationViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = OrganizationSerializer - permission_classes = [IsAuthenticated] + permission_classes = [DjangoObjectPermissions] queryset = Organization.objects.none() - permission_classes = (DjangoObjectPermissionsOrAnonReadOnly,) @extend_schema( request=None, diff --git a/documentcloud/users/tests/test_views.py b/documentcloud/users/tests/test_views.py index a937cf6f..35ba0550 100644 --- a/documentcloud/users/tests/test_views.py +++ b/documentcloud/users/tests/test_views.py @@ -123,7 +123,7 @@ def test_retrieve_me_expanded(self, client, user): def test_retrieve_me_anonymous(self, client): """me endpoint doesn't work for logged out users""" response = client.get("/api/users/me/") - assert response.status_code == status.HTTP_404_NOT_FOUND + assert response.status_code == status.HTTP_403_FORBIDDEN def test_update(self, client, user): """Test setting a users active org""" From 9b24ebd41ff5ac685628b67a964c3580aa9e9744 Mon Sep 17 00:00:00 2001 From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com> Date: Tue, 14 Apr 2026 13:05:15 -0500 Subject: [PATCH 2/2] Run isort --- documentcloud/organizations/views.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/documentcloud/organizations/views.py b/documentcloud/organizations/views.py index 68f339db..12a94a74 100644 --- a/documentcloud/organizations/views.py +++ b/documentcloud/organizations/views.py @@ -11,9 +11,7 @@ # DocumentCloud from documentcloud.addons.models import AddOnRun -from documentcloud.core.permissions import ( - OrganizationAICreditsPermissions -) +from documentcloud.core.permissions import OrganizationAICreditsPermissions from documentcloud.organizations.exceptions import InsufficientAICreditsError from documentcloud.organizations.models import Organization from documentcloud.organizations.serializers import (