Repository: bookwyrm-social/bookwyrm

Issue:
Private lists appear on Lists page
**Describe the bug**
When I go to `/list` and see the "All lists" tab, I can see the list name, cover images, and "Created and curated by" name for lists that are private. When clicking on the title, appropriately it returns a 404 as I should not be able to access these lists.

It seems to me that private lists should not be visible at all to anyone other than the list owner.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to `/lists` when logged in to bookwyrm.social
2. Observe that you can see lists with an envelope icon indicating they are private
3. Click on the name of one of these lists
4. See a 404 page

**Expected behavior**
I would expect private lists to be, well, private. Therefore they should not appear at all in the public Lists discovery page.

**Screenshots**
![Screen Shot 2022-07-02 at 6 36 45 pm](https://user-images.githubusercontent.com/17669239/176993299-7dfe388b-b1c2-4522-adc0-4f61afeb5f72.png)


**Instance**
bookwyrm.social

**Additional context**
Add any other context about the problem here.

---

**Desktop (please complete the following information):**
    n/a





Discussion:

Resolving patch:
diff --git a/bookwyrm/lists_stream.py b/bookwyrm/lists_stream.py
--- a/bookwyrm/lists_stream.py
+++ b/bookwyrm/lists_stream.py
@@ -114,12 +114,20 @@ def get_objects_for_store(self, store):
 
 @receiver(signals.post_save, sender=models.List)
 # pylint: disable=unused-argument
-def add_list_on_create(sender, instance, created, *args, **kwargs):
-    """add newly created lists streamsstreams"""
-    if not created:
+def add_list_on_create(sender, instance, created, *args, update_fields=None, **kwargs):
+    """add newly created lists streams"""
+    if created:
+        # when creating new things, gotta wait on the transaction
+        transaction.on_commit(lambda: add_list_on_create_command(instance.id))
         return
-    # when creating new things, gotta wait on the transaction
-    transaction.on_commit(lambda: add_list_on_create_command(instance.id))
+
+    # if update_fields was specified, we can check if privacy was updated, but if
+    # it wasn't specified (ie, by an activitypub update), there's no way to know
+    if update_fields and "privacy" not in update_fields:
+        return
+
+    # the privacy may have changed, so we need to re-do the whole thing
+    remove_list_task.delay(instance.id, re_add=True)
 
 
 @receiver(signals.post_delete, sender=models.List)
@@ -217,7 +225,7 @@ def populate_lists_task(user_id):
 
 
 @app.task(queue=MEDIUM)
-def remove_list_task(list_id):
+def remove_list_task(list_id, re_add=False):
     """remove a list from any stream it might be in"""
     stores = models.User.objects.filter(local=True, is_active=True).values_list(
         "id", flat=True
@@ -227,6 +235,9 @@ def remove_list_task(list_id):
     stores = [ListsStream().stream_id(idx) for idx in stores]
     ListsStream().remove_object_from_related_stores(list_id, stores=stores)
 
+    if re_add:
+        add_list_task.delay(list_id)
+
 
 @app.task(queue=HIGH)
 def add_list_task(list_id):
diff --git a/bookwyrm/models/list.py b/bookwyrm/models/list.py
--- a/bookwyrm/models/list.py
+++ b/bookwyrm/models/list.py
@@ -129,7 +129,7 @@ def save(self, *args, **kwargs):
         """on save, update embed_key and avoid clash with existing code"""
         if not self.embed_key:
             self.embed_key = uuid.uuid4()
-        return super().save(*args, **kwargs)
+        super().save(*args, **kwargs)
 
 
 class ListItem(CollectionItemMixin, BookWyrmModel):
@@ -156,7 +156,7 @@ def save(self, *args, **kwargs):
         super().save(*args, **kwargs)
         # tick the updated date on the parent list
         self.book_list.updated_date = timezone.now()
-        self.book_list.save(broadcast=False)
+        self.book_list.save(broadcast=False, update_fields=["updated_date"])
 
         list_owner = self.book_list.user
         model = apps.get_model("bookwyrm.Notification", require_ready=True)


Test patch:
diff --git a/bookwyrm/tests/lists_stream/test_signals.py b/bookwyrm/tests/lists_stream/test_signals.py
--- a/bookwyrm/tests/lists_stream/test_signals.py
+++ b/bookwyrm/tests/lists_stream/test_signals.py
@@ -32,9 +32,10 @@ def setUp(self):
 
     def test_add_list_on_create_command(self, _):
         """a new lists has entered"""
-        book_list = models.List.objects.create(
-            user=self.remote_user, name="hi", privacy="public"
-        )
+        with patch("bookwyrm.lists_stream.remove_list_task.delay"):
+            book_list = models.List.objects.create(
+                user=self.remote_user, name="hi", privacy="public"
+            )
         with patch("bookwyrm.lists_stream.add_list_task.delay") as mock:
             lists_stream.add_list_on_create_command(book_list.id)
         self.assertEqual(mock.call_count, 1)
@@ -43,9 +44,10 @@ def test_add_list_on_create_command(self, _):
 
     def test_remove_list_on_delete(self, _):
         """delete a list"""
-        book_list = models.List.objects.create(
-            user=self.remote_user, name="hi", privacy="public"
-        )
+        with patch("bookwyrm.lists_stream.remove_list_task.delay"):
+            book_list = models.List.objects.create(
+                user=self.remote_user, name="hi", privacy="public"
+            )
         with patch("bookwyrm.lists_stream.remove_list_task.delay") as mock:
             lists_stream.remove_list_on_delete(models.List, book_list)
         args = mock.call_args[0]
diff --git a/bookwyrm/tests/lists_stream/test_stream.py b/bookwyrm/tests/lists_stream/test_stream.py
--- a/bookwyrm/tests/lists_stream/test_stream.py
+++ b/bookwyrm/tests/lists_stream/test_stream.py
@@ -11,6 +11,7 @@
 @patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
 @patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
 @patch("bookwyrm.activitystreams.populate_stream_task.delay")
+@patch("bookwyrm.lists_stream.remove_list_task.delay")
 class ListsStream(TestCase):
     """using redis to build activity streams"""
 
diff --git a/bookwyrm/tests/lists_stream/test_tasks.py b/bookwyrm/tests/lists_stream/test_tasks.py
--- a/bookwyrm/tests/lists_stream/test_tasks.py
+++ b/bookwyrm/tests/lists_stream/test_tasks.py
@@ -35,7 +35,9 @@ def setUp(self):
                 inbox="https://example.com/users/rat/inbox",
                 outbox="https://example.com/users/rat/outbox",
             )
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             self.list = models.List.objects.create(
                 user=self.local_user, name="hi", privacy="public"
             )
diff --git a/bookwyrm/tests/models/test_group.py b/bookwyrm/tests/models/test_group.py
--- a/bookwyrm/tests/models/test_group.py
+++ b/bookwyrm/tests/models/test_group.py
@@ -80,7 +80,9 @@ def test_group_members_can_see_followers_only_lists(self, _):
         """follower-only group booklists should not be excluded from group booklist
         listing for group members who do not follower list owner"""
 
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             followers_list = models.List.objects.create(
                 name="Followers List",
                 curation="group",
@@ -101,8 +103,9 @@ def test_group_members_can_see_private_lists(self, _):
         """private group booklists should not be excluded from group booklist listing
         for group members"""
 
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
-
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             private_list = models.List.objects.create(
                 name="Private List",
                 privacy="direct",
diff --git a/bookwyrm/tests/models/test_list.py b/bookwyrm/tests/models/test_list.py
--- a/bookwyrm/tests/models/test_list.py
+++ b/bookwyrm/tests/models/test_list.py
@@ -23,7 +23,9 @@ def setUp(self):
 
     def test_remote_id(self, _):
         """shelves use custom remote ids"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             book_list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
@@ -32,7 +34,9 @@ def test_remote_id(self, _):
 
     def test_to_activity(self, _):
         """jsonify it"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             book_list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
@@ -46,7 +50,9 @@ def test_to_activity(self, _):
 
     def test_list_item(self, _):
         """a list entry"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             book_list = models.List.objects.create(
                 name="Test List", user=self.local_user, privacy="unlisted"
             )
@@ -64,7 +70,9 @@ def test_list_item(self, _):
 
     def test_list_item_pending(self, _):
         """a list entry"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             book_list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
@@ -84,7 +92,9 @@ def test_list_item_pending(self, _):
 
     def test_embed_key(self, _):
         """embed_key should never be empty"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             book_list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
diff --git a/bookwyrm/tests/views/inbox/test_inbox_remove.py b/bookwyrm/tests/views/inbox/test_inbox_remove.py
--- a/bookwyrm/tests/views/inbox/test_inbox_remove.py
+++ b/bookwyrm/tests/views/inbox/test_inbox_remove.py
@@ -75,7 +75,9 @@ def test_handle_unshelve_book(self):
 
     def test_handle_remove_book_from_list(self):
         """listing a book"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             booklist = models.List.objects.create(
                 name="test list",
                 user=self.local_user,
diff --git a/bookwyrm/tests/views/inbox/test_inbox_update.py b/bookwyrm/tests/views/inbox/test_inbox_update.py
--- a/bookwyrm/tests/views/inbox/test_inbox_update.py
+++ b/bookwyrm/tests/views/inbox/test_inbox_update.py
@@ -50,7 +50,9 @@ def setUp(self):
 
     def test_update_list(self):
         """a new list"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             book_list = models.List.objects.create(
                 name="hi", remote_id="https://example.com/list/22", user=self.local_user
             )
@@ -69,7 +71,8 @@ def test_update_list(self):
             "curation": "curated",
             "@context": "https://www.w3.org/ns/activitystreams",
         }
-        views.inbox.activity_task(activity)
+        with patch("bookwyrm.lists_stream.remove_list_task.delay"):
+            views.inbox.activity_task(activity)
         book_list.refresh_from_db()
         self.assertEqual(book_list.name, "Test List")
         self.assertEqual(book_list.curation, "curated")
diff --git a/bookwyrm/tests/views/lists/test_curate.py b/bookwyrm/tests/views/lists/test_curate.py
--- a/bookwyrm/tests/views/lists/test_curate.py
+++ b/bookwyrm/tests/views/lists/test_curate.py
@@ -36,7 +36,9 @@ def setUp(self):
             parent_work=work,
         )
 
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             self.list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
diff --git a/bookwyrm/tests/views/lists/test_embed.py b/bookwyrm/tests/views/lists/test_embed.py
--- a/bookwyrm/tests/views/lists/test_embed.py
+++ b/bookwyrm/tests/views/lists/test_embed.py
@@ -36,7 +36,9 @@ def setUp(self):
             parent_work=work,
         )
 
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             self.list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
diff --git a/bookwyrm/tests/views/lists/test_list.py b/bookwyrm/tests/views/lists/test_list.py
--- a/bookwyrm/tests/views/lists/test_list.py
+++ b/bookwyrm/tests/views/lists/test_list.py
@@ -65,7 +65,9 @@ def setUp(self):
             parent_work=work_four,
         )
 
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             self.list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
@@ -244,7 +246,7 @@ def test_list_edit(self):
 
         with patch(
             "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
-        ) as mock:
+        ) as mock, patch("bookwyrm.lists_stream.remove_list_task.delay"):
             result = view(request, self.list.id)
 
         self.assertEqual(mock.call_count, 1)
@@ -596,7 +598,7 @@ def test_add_three_books_and_move_last_to_first(self):
     def test_add_book_outsider(self):
         """put a book on a list"""
         self.list.curation = "open"
-        self.list.save(broadcast=False)
+        self.list.save(broadcast=False, update_fields=["curation"])
         request = self.factory.post(
             "",
             {
@@ -625,7 +627,7 @@ def test_add_book_outsider(self):
     def test_add_book_pending(self):
         """put a book on a list awaiting approval"""
         self.list.curation = "curated"
-        self.list.save(broadcast=False)
+        self.list.save(broadcast=False, update_fields=["curation"])
         request = self.factory.post(
             "",
             {
@@ -658,7 +660,7 @@ def test_add_book_pending(self):
     def test_add_book_self_curated(self):
         """put a book on a list automatically approved"""
         self.list.curation = "curated"
-        self.list.save(broadcast=False)
+        self.list.save(broadcast=False, update_fields=["curation"])
         request = self.factory.post(
             "",
             {
@@ -687,7 +689,7 @@ def test_add_book_self_curated(self):
     def test_add_book_permission_denied(self):
         """you can't add to that list"""
         self.list.curation = "closed"
-        self.list.save(broadcast=False)
+        self.list.save(broadcast=False, update_fields=["curation"])
         request = self.factory.post(
             "",
             {
diff --git a/bookwyrm/tests/views/lists/test_list_item.py b/bookwyrm/tests/views/lists/test_list_item.py
--- a/bookwyrm/tests/views/lists/test_list_item.py
+++ b/bookwyrm/tests/views/lists/test_list_item.py
@@ -32,7 +32,9 @@ def setUp(self):
             remote_id="https://example.com/book/1",
             parent_work=work,
         )
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             self.list = models.List.objects.create(
                 name="Test List", user=self.local_user
             )
diff --git a/bookwyrm/tests/views/lists/test_lists.py b/bookwyrm/tests/views/lists/test_lists.py
--- a/bookwyrm/tests/views/lists/test_lists.py
+++ b/bookwyrm/tests/views/lists/test_lists.py
@@ -39,7 +39,9 @@ def test_lists_page(self, _):
         view = views.Lists.as_view()
         with patch(
             "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
-        ), patch("bookwyrm.lists_stream.add_list_task.delay"):
+        ), patch("bookwyrm.lists_stream.add_list_task.delay"), patch(
+            "bookwyrm.lists_stream.remove_list_task.delay"
+        ):
             models.List.objects.create(name="Public list", user=self.local_user)
             models.List.objects.create(
                 name="Private list", privacy="direct", user=self.local_user
@@ -62,7 +64,9 @@ def test_lists_page(self, _):
     def test_saved_lists_page(self):
         """there are so many views, this just makes sure it LOADS"""
         view = views.SavedLists.as_view()
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             booklist = models.List.objects.create(
                 name="Public list", user=self.local_user
             )
@@ -82,7 +86,9 @@ def test_saved_lists_page(self):
     def test_saved_lists_page_empty(self):
         """there are so many views, this just makes sure it LOADS"""
         view = views.SavedLists.as_view()
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             models.List.objects.create(name="Public list", user=self.local_user)
             models.List.objects.create(
                 name="Private list", privacy="direct", user=self.local_user
@@ -108,7 +114,9 @@ def test_saved_lists_page_logged_out(self):
     def test_user_lists_page(self):
         """there are so many views, this just makes sure it LOADS"""
         view = views.UserLists.as_view()
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             models.List.objects.create(name="Public list", user=self.local_user)
             models.List.objects.create(
                 name="Private list", privacy="direct", user=self.local_user
@@ -146,7 +154,7 @@ def test_lists_create(self):
         request.user = self.local_user
         with patch(
             "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
-        ) as mock:
+        ) as mock, patch("bookwyrm.lists_stream.remove_list_task.delay"):
             result = view(request)
 
         self.assertEqual(mock.call_count, 1)
diff --git a/bookwyrm/tests/views/test_search.py b/bookwyrm/tests/views/test_search.py
--- a/bookwyrm/tests/views/test_search.py
+++ b/bookwyrm/tests/views/test_search.py
@@ -140,7 +140,9 @@ def test_search_users_logged_out(self):
 
     def test_search_lists(self):
         """searches remote connectors"""
-        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
+        with patch(
+            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
+        ), patch("bookwyrm.lists_stream.remove_list_task.delay"):
             booklist = models.List.objects.create(
                 user=self.local_user, name="test list"
             )