diff --git a/sas/api.py b/sas/api.py index 6f68bd94..412db414 100644 --- a/sas/api.py +++ b/sas/api.py @@ -143,11 +143,14 @@ class PicturesController(ControllerBase): "/{picture_id}/identified", permissions=[IsAuthenticated, CanView], response=list[IdentifiedUserSchema], + url_name="picture_identifications", ) def fetch_identifications(self, picture_id: int): """Fetch the users that have been identified on the given picture.""" picture = self.get_object_or_exception(Picture, pk=picture_id) - return picture.people.select_related("user") + return picture.people.viewable_by(self.context.request.user).select_related( + "user" + ) @route.put("/{picture_id}/identified", permissions=[IsAuthenticated, CanView]) def identify_users(self, picture_id: NonNegativeInt, users: set[NonNegativeInt]): diff --git a/sas/models.py b/sas/models.py index 0355c7da..0e8d5b88 100644 --- a/sas/models.py +++ b/sas/models.py @@ -265,6 +265,17 @@ def sas_notification_callback(notif: Notification): notif.param = str(count) +class PeoplePictureRelationQuerySet(models.QuerySet): + def viewable_by(self, user: User) -> Self: + if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID): + return self + if user.was_subscribed: + return self.filter( + Q(user_id=user.id) | Q(user__is_subscriber_viewable=True) + ) + return self.filter(user_id=user.id) + + class PeoplePictureRelation(models.Model): """The PeoplePictureRelation class makes the connection between User and Picture.""" @@ -281,6 +292,8 @@ class PeoplePictureRelation(models.Model): on_delete=models.CASCADE, ) + objects = PeoplePictureRelationQuerySet.as_manager() + class Meta: unique_together = ["user", "picture"] diff --git a/sas/tests/test_api.py b/sas/tests/test_api.py index 6c074dd0..1c5bb5ac 100644 --- a/sas/tests/test_api.py +++ b/sas/tests/test_api.py @@ -186,6 +186,29 @@ class TestPictureRelation(TestSas): assert res.status_code == 404 assert PeoplePictureRelation.objects.count() == relation_count + def test_fetch_relations_including_hidden_users(self): + """Test that normal subscribers users cannot see hidden profiles""" + picture = self.album_a.children_pictures.last() + self.user_a.is_subscriber_viewable = False + self.user_a.save() + url = reverse("api:picture_identifications", kwargs={"picture_id": picture.id}) + + # a normal subscriber user shouldn't see user_a as identified + self.client.force_login(subscriber_user.make()) + response = self.client.get(url) + data = {user["user"]["id"] for user in response.json()} + assert data == {self.user_b.id, self.user_c.id} + + # an admin should see everyone + self.client.force_login( + baker.make( + User, groups=[Group.objects.get(id=settings.SITH_GROUP_SAS_ADMIN_ID)] + ) + ) + response = self.client.get(url) + data = {user["user"]["id"] for user in response.json()} + assert data == {self.user_a.id, self.user_b.id, self.user_c.id} + class TestPictureModeration(TestSas): @classmethod diff --git a/sas/tests/test_model.py b/sas/tests/test_model.py index dd2299f1..5a5c5fe8 100644 --- a/sas/tests/test_model.py +++ b/sas/tests/test_model.py @@ -1,10 +1,11 @@ +import pytest from django.test import TestCase from model_bakery import baker from core.baker_recipes import old_subscriber_user, subscriber_user from core.models import User from sas.baker_recipes import picture_recipe -from sas.models import Picture +from sas.models import PeoplePictureRelation, Picture class TestPictureQuerySet(TestCase): @@ -44,3 +45,25 @@ class TestPictureQuerySet(TestCase): user.pictures.create(picture=self.pictures[1]) # moderated pictures = list(Picture.objects.viewable_by(user)) assert pictures == [self.pictures[1]] + + +@pytest.mark.django_db +def test_identifications_viewable_by_user(): + picture = baker.make(Picture) + identifications = baker.make( + PeoplePictureRelation, picture=picture, _quantity=10, _bulk_create=True + ) + identifications[0].user.is_subscriber_viewable = False + identifications[0].user.save() + + assert ( + list(picture.people.viewable_by(old_subscriber_user.make())) + == identifications[1:] + ) + assert ( + list(picture.people.viewable_by(baker.make(User, is_superuser=True))) + == identifications + ) + assert list(picture.people.viewable_by(identifications[1].user)) == [ + identifications[1] + ]