ActivityPub Controller: Actually pass for_user to following/followers
authorrinpatch <rinpatch@sdf.org>
Fri, 12 Jul 2019 17:54:20 +0000 (20:54 +0300)
committerrinpatch <rinpatch@sdf.org>
Fri, 12 Jul 2019 17:54:20 +0000 (20:54 +0300)
views and give 403 errors when trying to request hidden follower pages
when unauthenticated

lib/pleroma/web/activity_pub/activity_pub_controller.ex
lib/pleroma/web/router.ex
test/web/activity_pub/activity_pub_controller_test.exs

index cf517620182e039a82bfedb32721ddd5e47972c9..e2af4ad1a7167286ef0cc9574ae7c9936d5689ea 100644 (file)
@@ -103,43 +103,57 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
     end
   end
 
-  def following(conn, %{"nickname" => nickname, "page" => page}) do
+  def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, user} <- User.ensure_keys_present(user) do
+         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
+         {:show_follows, true} <-
+           {:show_follows, (for_user && for_user == user) || !user.info.hide_follows} do
       {page, _} = Integer.parse(page)
 
       conn
       |> put_resp_header("content-type", "application/activity+json")
-      |> json(UserView.render("following.json", %{user: user, page: page}))
+      |> json(UserView.render("following.json", %{user: user, page: page, for: for_user}))
+    else
+      {:show_follows, _} ->
+        conn
+        |> put_resp_header("content-type", "application/activity+json")
+        |> send_resp(403, "")
     end
   end
 
-  def following(conn, %{"nickname" => nickname}) do
+  def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, user} <- User.ensure_keys_present(user) do
+         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
       conn
       |> put_resp_header("content-type", "application/activity+json")
-      |> json(UserView.render("following.json", %{user: user}))
+      |> json(UserView.render("following.json", %{user: user, for: for_user}))
     end
   end
 
-  def followers(conn, %{"nickname" => nickname, "page" => page}) do
+  def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, user} <- User.ensure_keys_present(user) do
+         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
+         {:show_followers, true} <-
+           {:show_followers, (for_user && for_user == user) || !user.info.hide_followers} do
       {page, _} = Integer.parse(page)
 
       conn
       |> put_resp_header("content-type", "application/activity+json")
-      |> json(UserView.render("followers.json", %{user: user, page: page}))
+      |> json(UserView.render("followers.json", %{user: user, page: page, for: for_user}))
+    else
+      {:show_followers, _} ->
+        conn
+        |> put_resp_header("content-type", "application/activity+json")
+        |> send_resp(403, "")
     end
   end
 
-  def followers(conn, %{"nickname" => nickname}) do
+  def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
     with %User{} = user <- User.get_cached_by_nickname(nickname),
-         {:ok, user} <- User.ensure_keys_present(user) do
+         {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
       conn
       |> put_resp_header("content-type", "application/activity+json")
-      |> json(UserView.render("followers.json", %{user: user}))
+      |> json(UserView.render("followers.json", %{user: user, for: for_user}))
     end
   end
 
@@ -325,4 +339,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
 
     conn
   end
+
+  defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
+    {:ok, new_user} = User.ensure_keys_present(user)
+
+    for_user =
+      if new_user != user and match?(%User{}, for_user) do
+        User.get_cached_by_nickname(for_user.nickname)
+      else
+        for_user
+      end
+
+    {new_user, for_user}
+  end
 end
index d53fa8a350b56c1f9387af3d6a76d92c0462c1af..e03a3a2e5acb5d06192fad08c1e8b2451446ccfc 100644 (file)
@@ -623,8 +623,6 @@ defmodule Pleroma.Web.Router do
     # XXX: not really ostatus
     pipe_through(:ostatus)
 
-    get("/users/:nickname/followers", ActivityPubController, :followers)
-    get("/users/:nickname/following", ActivityPubController, :following)
     get("/users/:nickname/outbox", ActivityPubController, :outbox)
     get("/objects/:uuid/likes", ActivityPubController, :object_likes)
   end
@@ -656,6 +654,12 @@ defmodule Pleroma.Web.Router do
       pipe_through(:oauth_write)
       post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
     end
+
+    scope [] do
+      pipe_through(:oauth_read_or_public)
+      get("/users/:nickname/followers", ActivityPubController, :followers)
+      get("/users/:nickname/following", ActivityPubController, :following)
+    end
   end
 
   scope "/relay", Pleroma.Web.ActivityPub do
index cbc25bcdb46fe7afa1de965c476680ac377c0fcc..452172bb49bbbbcc2fd4390bcb4f686c06ba33f3 100644 (file)
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
   alias Pleroma.Web.ActivityPub.ObjectView
   alias Pleroma.Web.ActivityPub.UserView
   alias Pleroma.Web.ActivityPub.Utils
+  alias Pleroma.Web.CommonAPI
 
   setup_all do
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -564,6 +565,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       assert is_binary(result["first"])
     end
 
+    test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated",
+         %{conn: conn} do
+      user = insert(:user, %{info: %{hide_followers: true}})
+
+      result =
+        conn
+        |> get("/users/#{user.nickname}/followers?page=1")
+
+      assert result.status == 403
+      assert result.resp_body == ""
+    end
+
+    test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
+         %{conn: conn} do
+      user = insert(:user, %{info: %{hide_followers: true}})
+      other_user = insert(:user)
+      {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
+
+      result =
+        conn
+        |> assign(:user, user)
+        |> get("/users/#{user.nickname}/followers?page=1")
+        |> json_response(200)
+
+      assert result["totalItems"] == 1
+      assert result["orderedItems"] == [other_user.ap_id]
+    end
+
     test "it works for more than 10 users", %{conn: conn} do
       user = insert(:user)
 
@@ -618,6 +647,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       assert is_binary(result["first"])
     end
 
+    test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated",
+         %{conn: conn} do
+      user = insert(:user, %{info: %{hide_follows: true}})
+
+      result =
+        conn
+        |> get("/users/#{user.nickname}/following?page=1")
+
+      assert result.status == 403
+      assert result.resp_body == ""
+    end
+
+    test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
+         %{conn: conn} do
+      user = insert(:user, %{info: %{hide_follows: true}})
+      other_user = insert(:user)
+      {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
+
+      result =
+        conn
+        |> assign(:user, user)
+        |> get("/users/#{user.nickname}/following?page=1")
+        |> json_response(200)
+
+      assert result["totalItems"] == 1
+      assert result["orderedItems"] == [other_user.ap_id]
+    end
+
     test "it works for more than 10 users", %{conn: conn} do
       user = insert(:user)