Merge branch 'feature/1206-restrict-unauthenticated' into 'develop'
authorlain <lain@soykaf.club>
Fri, 20 Mar 2020 14:40:08 +0000 (14:40 +0000)
committerlain <lain@soykaf.club>
Fri, 20 Mar 2020 14:40:08 +0000 (14:40 +0000)
restrict_unauthenticated setting

Closes #1206

See merge request pleroma/pleroma!2318

12 files changed:
CHANGELOG.md
config/config.exs
config/description.exs
docs/configuration/cheatsheet.md
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/visibility.ex
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
lib/pleroma/web/mastodon_api/controllers/status_controller.ex
lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
test/web/mastodon_api/controllers/account_controller_test.exs
test/web/mastodon_api/controllers/status_controller_test.exs
test/web/mastodon_api/controllers/timeline_controller_test.exs

index a27200895c0813513776a6f35536758272c5e1a9..15a073c6488eb714d26ef4f6c25b706e7f7344e8 100644 (file)
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Added
 - NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list.
+- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
 <details>
   <summary>API Changes</summary>
 - Mastodon API: Support for `include_types` in `/api/v1/notifications`.
index 3357e23e724b5804f629cb921b69d73e6bf18dda..2ab9391074f9851c0ff85b29884a63b26da7f174 100644 (file)
@@ -624,6 +624,11 @@ config :pleroma, Pleroma.Repo,
   parameters: [gin_fuzzy_search_limit: "500"],
   prepare: :unnamed
 
+config :pleroma, :restrict_unauthenticated,
+  timelines: %{local: false, federated: false},
+  profiles: %{local: false, remote: false},
+  activities: %{local: false, remote: false}
+
 # Import environment specific config. This must remain at the bottom
 # of this file so it overrides the configuration defined above.
 import_config "#{Mix.env()}.exs"
index 732c767344387445d03304b19893bd15ec47a859..3781fb9cb7dcf27a1a5cce96456f879a6c8b0eb8 100644 (file)
@@ -2915,5 +2915,65 @@ config :pleroma, :config_description, [
         suggestions: [2]
       }
     ]
+  },
+  %{
+    group: :pleroma,
+    key: :restrict_unauthenticated,
+    type: :group,
+    description:
+      "Disallow viewing timelines, user profiles and statuses for unauthenticated users.",
+    children: [
+      %{
+        key: :timelines,
+        type: :map,
+        description: "Settings for public and federated timelines.",
+        children: [
+          %{
+            key: :local,
+            type: :boolean,
+            description: "Disallow view public timeline."
+          },
+          %{
+            key: :federated,
+            type: :boolean,
+            description: "Disallow view federated timeline."
+          }
+        ]
+      },
+      %{
+        key: :profiles,
+        type: :map,
+        description: "Settings for user profiles.",
+        children: [
+          %{
+            key: :local,
+            type: :boolean,
+            description: "Disallow view local user profiles."
+          },
+          %{
+            key: :remote,
+            type: :boolean,
+            description: "Disallow view remote user profiles."
+          }
+        ]
+      },
+      %{
+        key: :activities,
+        type: :map,
+        description: "Settings for statuses.",
+        children: [
+          %{
+            key: :local,
+            type: :boolean,
+            description: "Disallow view local statuses."
+          },
+          %{
+            key: :remote,
+            type: :boolean,
+            description: "Disallow view remote statuses."
+          }
+        ]
+      }
+    ]
   }
 ]
index 4012fe9b1ce44139e2e41ab48087ec1b7759cf2a..d16435e11bc5c9961dedea363b0a7e213bf94f6d 100644 (file)
@@ -872,3 +872,21 @@ config :auto_linker,
 ## :configurable_from_database
 
 Boolean, enables/disables in-database configuration. Read [Transfering the config to/from the database](../administration/CLI_tasks/config.md) for more information.
+
+
+
+## Restrict entities access for unauthenticated users
+
+### :restrict_unauthenticated
+
+Restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
+
+* `timelines` - public and federated timelines
+  * `local` - public timeline
+  * `federated`
+* `profiles` - user profiles
+  * `local`
+  * `remote`
+* `activities` - statuses
+  * `local`
+  * `remote`
\ No newline at end of file
index 911dde6e2876bc9fba1b7dc093554e872a1c3269..8693c0b8090c28c633a49d4110efdfba71c8d157 100644 (file)
@@ -237,7 +237,18 @@ defmodule Pleroma.User do
 
   def visible_for?(%User{invisible: true}, _), do: false
 
-  def visible_for?(%User{id: user_id}, %User{id: for_id}) when user_id == for_id, do: true
+  def visible_for?(%User{id: user_id}, %User{id: user_id}), do: true
+
+  def visible_for?(%User{local: local} = user, nil) do
+    cfg_key =
+      if local,
+        do: :local,
+        else: :remote
+
+    if Config.get([:restrict_unauthenticated, :profiles, cfg_key]),
+      do: false,
+      else: account_status(user) == :active
+  end
 
   def visible_for?(%User{} = user, for_user) do
     account_status(user) == :active || superuser?(for_user)
index 6f226fc927dd451eb78913e9bb7b10777a6ee3aa..453a6842edbc88d83f9bc740ddd1875c2b0aa590 100644 (file)
@@ -44,6 +44,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
   def is_list?(%{data: %{"listMessage" => _}}), do: true
   def is_list?(_), do: false
 
+  @spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
   def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
 
   def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
@@ -55,14 +56,21 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
 
   def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
 
-  def visible_for_user?(activity, nil) do
-    is_public?(activity)
+  def visible_for_user?(%{local: local} = activity, nil) do
+    cfg_key =
+      if local,
+        do: :local,
+        else: :remote
+
+    if Pleroma.Config.get([:restrict_unauthenticated, :activities, cfg_key]),
+      do: false,
+      else: is_public?(activity)
   end
 
   def visible_for_user?(activity, user) do
     x = [user.ap_id | User.following(user)]
     y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
-    visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
+    is_public?(activity) || Enum.any?(x, &(&1 in y))
   end
 
   def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
index 88c997b9f75e2510262c6a945f3d9de514a7c10c..6dbf11ac92ff080f470d7df30277ff52f0a945fc 100644 (file)
@@ -60,7 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
 
   plug(
     Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
-    when action != :create
+    when action not in [:create, :show, :statuses]
   )
 
   @relations [:follow, :unfollow]
@@ -259,7 +259,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
 
   @doc "GET /api/v1/accounts/:id/statuses"
   def statuses(%{assigns: %{user: reading_user}} = conn, params) do
-    with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do
+    with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user),
+         true <- User.visible_for?(user, reading_user) do
       params =
         params
         |> Map.put("tag", params["tagged"])
@@ -271,6 +272,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
       |> add_link_headers(activities)
       |> put_view(StatusView)
       |> render("index.json", activities: activities, for: reading_user, as: :activity)
+    else
+      _e -> render_error(conn, :not_found, "Can't find user")
     end
   end
 
index 5c90065f6f9298dbb207cab596786d65175baae7..37afe6949f29f1e116beb5f6c1d88fdf3c19852f 100644 (file)
@@ -76,7 +76,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
     %{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
   )
 
-  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
+  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :show])
 
   @rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
 
index 09e08271b10f0b210bbab8c49af1033b9849b1f7..91f41416d4aad5381a1ee80c9989e0af34dfc905 100644 (file)
@@ -27,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
   plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
   plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
 
-  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
+  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :public)
 
   plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
 
@@ -75,17 +75,30 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
   def public(%{assigns: %{user: user}} = conn, params) do
     local_only = truthy_param?(params["local"])
 
-    activities =
-      params
-      |> Map.put("type", ["Create", "Announce"])
-      |> Map.put("local_only", local_only)
-      |> Map.put("blocking_user", user)
-      |> Map.put("muting_user", user)
-      |> ActivityPub.fetch_public_activities()
+    cfg_key =
+      if local_only do
+        :local
+      else
+        :federated
+      end
 
-    conn
-    |> add_link_headers(activities, %{"local" => local_only})
-    |> render("index.json", activities: activities, for: user, as: :activity)
+    restrict? = Pleroma.Config.get([:restrict_unauthenticated, :timelines, cfg_key])
+
+    if not (restrict? and is_nil(user)) do
+      activities =
+        params
+        |> Map.put("type", ["Create", "Announce"])
+        |> Map.put("local_only", local_only)
+        |> Map.put("blocking_user", user)
+        |> Map.put("muting_user", user)
+        |> ActivityPub.fetch_public_activities()
+
+      conn
+      |> add_link_headers(activities, %{"local" => local_only})
+      |> render("index.json", activities: activities, for: user, as: :activity)
+    else
+      render_error(conn, :unauthorized, "authorization required for timeline view")
+    end
   end
 
   def hashtag_fetching(params, user, local_only) do
index 7efccd9c4faca58f9c248d60374fcad8c8ec5283..2182dd28e6f87564bb8b50854c304005f3303091 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
   use Pleroma.Web.ConnCase
 
+  alias Pleroma.Config
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
@@ -46,7 +47,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "works by nickname for remote users" do
-      Pleroma.Config.put([:instance, :limit_to_local_content], false)
+      Config.put([:instance, :limit_to_local_content], false)
       user = insert(:user, nickname: "user@example.com", local: false)
 
       conn =
@@ -58,7 +59,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "respects limit_to_local_content == :all for remote user nicknames" do
-      Pleroma.Config.put([:instance, :limit_to_local_content], :all)
+      Config.put([:instance, :limit_to_local_content], :all)
 
       user = insert(:user, nickname: "user@example.com", local: false)
 
@@ -70,7 +71,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
-      Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
+      Config.put([:instance, :limit_to_local_content], :unauthenticated)
 
       user = insert(:user, nickname: "user@example.com", local: false)
       reading_user = insert(:user)
@@ -140,6 +141,106 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
   end
 
+  defp local_and_remote_users do
+    local = insert(:user)
+    remote = insert(:user, local: false)
+    {:ok, local: local, remote: remote}
+  end
+
+  describe "user fetching with restrict unauthenticated profiles for local and remote" do
+    setup do: local_and_remote_users()
+
+    clear_config([:restrict_unauthenticated, :profiles, :local]) do
+      Config.put([:restrict_unauthenticated, :profiles, :local], true)
+    end
+
+    clear_config([:restrict_unauthenticated, :profiles, :remote]) do
+      Config.put([:restrict_unauthenticated, :profiles, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+  end
+
+  describe "user fetching with restrict unauthenticated profiles for local" do
+    setup do: local_and_remote_users()
+
+    clear_config([:restrict_unauthenticated, :profiles, :local]) do
+      Config.put([:restrict_unauthenticated, :profiles, :local], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+  end
+
+  describe "user fetching with restrict unauthenticated profiles for remote" do
+    setup do: local_and_remote_users()
+
+    clear_config([:restrict_unauthenticated, :profiles, :remote]) do
+      Config.put([:restrict_unauthenticated, :profiles, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+  end
+
   describe "user timelines" do
     setup do: oauth_access(["read:statuses"])
 
@@ -293,6 +394,110 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
   end
 
+  defp local_and_remote_activities(%{local: local, remote: remote}) do
+    insert(:note_activity, user: local)
+    insert(:note_activity, user: remote, local: false)
+
+    :ok
+  end
+
+  describe "statuses with restrict unauthenticated profiles for local and remote" do
+    setup do: local_and_remote_users()
+    setup :local_and_remote_activities
+
+    clear_config([:restrict_unauthenticated, :profiles, :local]) do
+      Config.put([:restrict_unauthenticated, :profiles, :local], true)
+    end
+
+    clear_config([:restrict_unauthenticated, :profiles, :remote]) do
+      Config.put([:restrict_unauthenticated, :profiles, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+    end
+  end
+
+  describe "statuses with restrict unauthenticated profiles for local" do
+    setup do: local_and_remote_users()
+    setup :local_and_remote_activities
+
+    clear_config([:restrict_unauthenticated, :profiles, :local]) do
+      Config.put([:restrict_unauthenticated, :profiles, :local], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+    end
+  end
+
+  describe "statuses with restrict unauthenticated profiles for remote" do
+    setup do: local_and_remote_users()
+    setup :local_and_remote_activities
+
+    clear_config([:restrict_unauthenticated, :profiles, :remote]) do
+      Config.put([:restrict_unauthenticated, :profiles, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Can't find user"
+             }
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
+      assert length(json_response(res_conn, 200)) == 1
+    end
+  end
+
   describe "followers" do
     setup do: oauth_access(["read:accounts"])
 
@@ -757,7 +962,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
 
   describe "create account by app / rate limit" do
     clear_config([:rate_limit, :app_account_creation]) do
-      Pleroma.Config.put([:rate_limit, :app_account_creation], {10_000, 2})
+      Config.put([:rate_limit, :app_account_creation], {10_000, 2})
     end
 
     test "respects rate limit setting", %{conn: conn} do
index fbf63f608be841266a7889ca7d8891b759dff27e..81513a429b6d20c63c612e8a3b29d182620d0f1d 100644 (file)
@@ -476,6 +476,103 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     assert id == to_string(activity.id)
   end
 
+  defp local_and_remote_activities do
+    local = insert(:note_activity)
+    remote = insert(:note_activity, local: false)
+    {:ok, local: local, remote: remote}
+  end
+
+  describe "status with restrict unauthenticated activities for local and remote" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :activities, :local]) do
+      Config.put([:restrict_unauthenticated, :activities, :local], true)
+    end
+
+    clear_config([:restrict_unauthenticated, :activities, :remote]) do
+      Config.put([:restrict_unauthenticated, :activities, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/statuses/#{local.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Record not found"
+             }
+
+      res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Record not found"
+             }
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+      res_conn = get(conn, "/api/v1/statuses/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+  end
+
+  describe "status with restrict unauthenticated activities for local" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :activities, :local]) do
+      Config.put([:restrict_unauthenticated, :activities, :local], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/statuses/#{local.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Record not found"
+             }
+
+      res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+      res_conn = get(conn, "/api/v1/statuses/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+  end
+
+  describe "status with restrict unauthenticated activities for remote" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :activities, :remote]) do
+      Config.put([:restrict_unauthenticated, :activities, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/statuses/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
+
+      assert json_response(res_conn, :not_found) == %{
+               "error" => "Record not found"
+             }
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+      res_conn = get(conn, "/api/v1/statuses/#{local.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+
+      res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
+      assert %{"id" => _} = json_response(res_conn, 200)
+    end
+  end
+
   test "getting a status that doesn't exist returns 404" do
     %{conn: conn} = oauth_access(["read:statuses"])
     activity = insert(:note_activity)
@@ -514,6 +611,78 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"])
   end
 
+  describe "getting statuses by ids with restricted unauthenticated for local and remote" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :activities, :local]) do
+      Config.put([:restrict_unauthenticated, :activities, :local], true)
+    end
+
+    clear_config([:restrict_unauthenticated, :activities, :remote]) do
+      Config.put([:restrict_unauthenticated, :activities, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]})
+
+      assert json_response(res_conn, 200) == []
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]})
+
+      assert length(json_response(res_conn, 200)) == 2
+    end
+  end
+
+  describe "getting statuses by ids with restricted unauthenticated for local" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :activities, :local]) do
+      Config.put([:restrict_unauthenticated, :activities, :local], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]})
+
+      remote_id = remote.id
+      assert [%{"id" => ^remote_id}] = json_response(res_conn, 200)
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]})
+
+      assert length(json_response(res_conn, 200)) == 2
+    end
+  end
+
+  describe "getting statuses by ids with restricted unauthenticated for remote" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :activities, :remote]) do
+      Config.put([:restrict_unauthenticated, :activities, :remote], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
+      res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]})
+
+      local_id = local.id
+      assert [%{"id" => ^local_id}] = json_response(res_conn, 200)
+    end
+
+    test "if user is authenticated", %{local: local, remote: remote} do
+      %{conn: conn} = oauth_access(["read"])
+
+      res_conn = get(conn, "/api/v1/statuses", %{ids: [local.id, remote.id]})
+
+      assert length(json_response(res_conn, 200)) == 2
+    end
+  end
+
   describe "deleting a status" do
     test "when you created it" do
       %{user: author, conn: conn} = oauth_access(["write:statuses"])
index 2c03b0a751122500dcdd9022179a67ff8facaad6..a15c759d4ecdfc7ad5f2a35264b3a41f9a80c633 100644 (file)
@@ -12,8 +12,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
 
-  clear_config([:instance, :public])
-
   setup do
     mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
     :ok
@@ -80,15 +78,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       assert [%{"content" => "test"}] = json_response(conn, :ok)
     end
 
-    test "the public timeline when public is set to false", %{conn: conn} do
-      Config.put([:instance, :public], false)
-
-      assert %{"error" => "This resource requires authentication."} ==
-               conn
-               |> get("/api/v1/timelines/public", %{"local" => "False"})
-               |> json_response(:forbidden)
-    end
-
     test "the public timeline includes only public statuses for an authenticated user" do
       %{user: user, conn: conn} = oauth_access(["read:statuses"])
 
@@ -102,6 +91,106 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
     end
   end
 
+  defp local_and_remote_activities do
+    insert(:note_activity)
+    insert(:note_activity, local: false)
+    :ok
+  end
+
+  describe "public with restrict unauthenticated timeline for local and federated timelines" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :timelines, :local]) do
+      Config.put([:restrict_unauthenticated, :timelines, :local], true)
+    end
+
+    clear_config([:restrict_unauthenticated, :timelines, :federated]) do
+      Config.put([:restrict_unauthenticated, :timelines, :federated], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn} do
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"})
+
+      assert json_response(res_conn, :unauthorized) == %{
+               "error" => "authorization required for timeline view"
+             }
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"})
+
+      assert json_response(res_conn, :unauthorized) == %{
+               "error" => "authorization required for timeline view"
+             }
+    end
+
+    test "if user is authenticated" do
+      %{conn: conn} = oauth_access(["read:statuses"])
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"})
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"})
+      assert length(json_response(res_conn, 200)) == 2
+    end
+  end
+
+  describe "public with restrict unauthenticated timeline for local" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :timelines, :local]) do
+      Config.put([:restrict_unauthenticated, :timelines, :local], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn} do
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"})
+
+      assert json_response(res_conn, :unauthorized) == %{
+               "error" => "authorization required for timeline view"
+             }
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"})
+      assert length(json_response(res_conn, 200)) == 2
+    end
+
+    test "if user is authenticated", %{conn: _conn} do
+      %{conn: conn} = oauth_access(["read:statuses"])
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"})
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"})
+      assert length(json_response(res_conn, 200)) == 2
+    end
+  end
+
+  describe "public with restrict unauthenticated timeline for remote" do
+    setup do: local_and_remote_activities()
+
+    clear_config([:restrict_unauthenticated, :timelines, :federated]) do
+      Config.put([:restrict_unauthenticated, :timelines, :federated], true)
+    end
+
+    test "if user is unauthenticated", %{conn: conn} do
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"})
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"})
+
+      assert json_response(res_conn, :unauthorized) == %{
+               "error" => "authorization required for timeline view"
+             }
+    end
+
+    test "if user is authenticated", %{conn: _conn} do
+      %{conn: conn} = oauth_access(["read:statuses"])
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"})
+      assert length(json_response(res_conn, 200)) == 1
+
+      res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"})
+      assert length(json_response(res_conn, 200)) == 2
+    end
+  end
+
   describe "direct" do
     test "direct timeline", %{conn: conn} do
       user_one = insert(:user)