From 95529ab709b14acbf0b4ef2c17a76e0540e1e84e Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Fri, 14 Aug 2020 20:55:45 +0300
Subject: [PATCH 1/2] [#2046] Defaulted pleroma/restrict_unauthenticated basing
 on instance privacy setting (i.e. restrict on private instances only by
 default).

---
 config/config.exs                             |  8 ++++--
 lib/pleroma/config.ex                         | 10 +++++++
 lib/pleroma/user.ex                           |  8 ++++--
 lib/pleroma/web/activity_pub/visibility.ex    |  7 ++---
 .../controllers/timeline_controller.ex        |  5 ++--
 lib/pleroma/web/preload/timelines.ex          |  2 +-
 test/web/preload/timeline_test.exs            | 28 ++++---------------
 7 files changed, 31 insertions(+), 37 deletions(-)

diff --git a/config/config.exs b/config/config.exs
index eb85a6ed4..a7c9e54b1 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -725,10 +725,12 @@
     timeout: 300_000
   ]
 
+private_instance? = :if_instance_is_private
+
 config :pleroma, :restrict_unauthenticated,
-  timelines: %{local: false, federated: false},
-  profiles: %{local: false, remote: false},
-  activities: %{local: false, remote: false}
+  timelines: %{local: private_instance?, federated: private_instance?},
+  profiles: %{local: private_instance?, remote: private_instance?},
+  activities: %{local: private_instance?, remote: private_instance?}
 
 config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
 
diff --git a/lib/pleroma/config.ex b/lib/pleroma/config.ex
index a8329cc1e..97f877595 100644
--- a/lib/pleroma/config.ex
+++ b/lib/pleroma/config.ex
@@ -81,6 +81,16 @@ def delete(key) do
     Application.delete_env(:pleroma, key)
   end
 
+  def restrict_unauthenticated_access?(resource, kind) do
+    setting = get([:restrict_unauthenticated, resource, kind])
+
+    if setting in [nil, :if_instance_is_private] do
+      !get!([:instance, :public])
+    else
+      setting
+    end
+  end
+
   def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], [])
 
   def oauth_consumer_enabled?, do: oauth_consumer_strategies() != []
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index d1436a688..ac065e9dc 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -311,10 +311,12 @@ def visible_for(%User{} = user, for_user) do
 
   def visible_for(_, _), do: :invisible
 
-  defp restrict_unauthenticated?(%User{local: local}) do
-    config_key = if local, do: :local, else: :remote
+  defp restrict_unauthenticated?(%User{local: true}) do
+    Config.restrict_unauthenticated_access?(:profiles, :local)
+  end
 
-    Config.get([:restrict_unauthenticated, :profiles, config_key], false)
+  defp restrict_unauthenticated?(%User{local: _}) do
+    Config.restrict_unauthenticated_access?(:profiles, :remote)
   end
 
   defp visible_account_status(user) do
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 343f41caa..5c349bb7a 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -59,12 +59,9 @@ def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{
   end
 
   def visible_for_user?(%{local: local} = activity, nil) do
-    cfg_key =
-      if local,
-        do: :local,
-        else: :remote
+    cfg_key = if local, do: :local, else: :remote
 
-    if Pleroma.Config.get([:restrict_unauthenticated, :activities, cfg_key]),
+    if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key),
       do: false,
       else: is_public?(activity)
   end
diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
index ab7b1d6aa..9244316ed 100644
--- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
   import Pleroma.Web.ControllerHelper,
     only: [add_link_headers: 2, add_link_headers: 3]
 
+  alias Pleroma.Config
   alias Pleroma.Pagination
   alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
   alias Pleroma.Plugs.OAuthScopesPlug
@@ -89,11 +90,11 @@ def direct(%{assigns: %{user: user}} = conn, params) do
   end
 
   defp restrict_unauthenticated?(true = _local_only) do
-    Pleroma.Config.get([:restrict_unauthenticated, :timelines, :local])
+    Config.restrict_unauthenticated_access?(:timelines, :local)
   end
 
   defp restrict_unauthenticated?(_) do
-    Pleroma.Config.get([:restrict_unauthenticated, :timelines, :federated])
+    Config.restrict_unauthenticated_access?(:timelines, :federated)
   end
 
   # GET /api/v1/timelines/public
diff --git a/lib/pleroma/web/preload/timelines.ex b/lib/pleroma/web/preload/timelines.ex
index 57de04051..b279a865d 100644
--- a/lib/pleroma/web/preload/timelines.ex
+++ b/lib/pleroma/web/preload/timelines.ex
@@ -16,7 +16,7 @@ def generate_terms(params) do
   end
 
   def build_public_tag(acc, params) do
-    if Pleroma.Config.get([:restrict_unauthenticated, :timelines, :federated], true) do
+    if Pleroma.Config.restrict_unauthenticated_access?(:timelines, :federated) do
       acc
     else
       Map.put(acc, @public_url, public_timeline(params))
diff --git a/test/web/preload/timeline_test.exs b/test/web/preload/timeline_test.exs
index fea95a6a4..3b1f2f1aa 100644
--- a/test/web/preload/timeline_test.exs
+++ b/test/web/preload/timeline_test.exs
@@ -12,16 +12,8 @@ defmodule Pleroma.Web.Preload.Providers.TimelineTest do
   @public_url "/api/v1/timelines/public"
 
   describe "unauthenticated timeliness when restricted" do
-    setup do
-      svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines])
-      Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{local: true, federated: true})
-
-      on_exit(fn ->
-        Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config)
-      end)
-
-      :ok
-    end
+    setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
+    setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
 
     test "return nothing" do
       tl_data = Timelines.generate_terms(%{})
@@ -31,20 +23,10 @@ test "return nothing" do
   end
 
   describe "unauthenticated timeliness when unrestricted" do
-    setup do
-      svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines])
+    setup do: clear_config([:restrict_unauthenticated, :timelines, :local], false)
+    setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], false)
 
-      Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{
-        local: false,
-        federated: false
-      })
-
-      on_exit(fn ->
-        Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config)
-      end)
-
-      {:ok, user: insert(:user)}
-    end
+    setup do: {:ok, user: insert(:user)}
 
     test "returns the timeline when not restricted" do
       assert Timelines.generate_terms(%{})

From 60ac83a4c196233ed13c3da9ca296b0a4224e9a3 Mon Sep 17 00:00:00 2001
From: Ivan Tashkinov <ivantashkinov@gmail.com>
Date: Sat, 15 Aug 2020 18:30:20 +0300
Subject: [PATCH 2/2] [#2046] Added test for pleroma/restrict_unauthenticated
 defaults on private instance. Updated docs and changelog.

---
 CHANGELOG.md                                    |  1 +
 docs/configuration/cheatsheet.md                |  6 ++++--
 .../controllers/timeline_controller_test.exs    | 17 +++++++++++++++++
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8e80eb3c..d0fa138df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Configuration: `:media_proxy, whitelist` format changed to host with scheme (e.g. `http://example.com` instead of `example.com`). Domain format is deprecated.
 - **Breaking:** Configuration: `:instance, welcome_user_nickname` moved to `:welcome, :direct_message, :sender_nickname`, `:instance, :welcome_message` moved to `:welcome, :direct_message, :message`. Old config namespace is deprecated.
 - **Breaking:** LDAP: Fallback to local database authentication has been removed for security reasons and lack of a mechanism to ensure the passwords are synchronized when LDAP passwords are updated.
+- **Breaking** Changed defaults for `:restrict_unauthenticated` so that when `:instance, :public` is set to `false` then all `:restrict_unauthenticated` items be effectively set to `true`. If you'd like to allow unauthenticated access to specific API endpoints on a private instance, please explicitly set `:restrict_unauthenticated` to non-default value in `config/prod.secret.exs`. 
 
 <details>
   <summary>API Changes</summary>
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index e5742bc3a..e68b6c6dc 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -38,8 +38,8 @@ To add configuration to your config file, you can copy it from the base config.
 * `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
 * `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
 * `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance.
-* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. See also: `restrict_unauthenticated`.
-* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
+* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
+* `quarantined_instances`: List of ActivityPub instances where private (DMs, followers-only) activities will not be send.
 * `managed_config`: Whenether the config for pleroma-fe is configured in [:frontend_configurations](#frontend_configurations) or in ``static/config.json``.
 * `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
 * `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
@@ -1051,6 +1051,8 @@ Restrict access for unauthenticated users to timelines (public and federated), u
   * `local`
   * `remote`
 
+Note: when `:instance, :public` is set to `false`, all `:restrict_unauthenticated` items be effectively set to `true` by default. If you'd like to allow unauthenticated access to specific API endpoints on a private instance, please explicitly set `:restrict_unauthenticated` to non-default value in `config/prod.secret.exs`.
+
 Note: setting `restrict_unauthenticated/timelines/local` to `true` has no practical sense if `restrict_unauthenticated/timelines/federated` is set to `false` (since local public activities will still be delivered to unauthenticated users as part of federated timeline).
 
 ## Pleroma.Web.ApiSpec.CastAndValidate
diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs
index 50e0d783d..71bac99f7 100644
--- a/test/web/mastodon_api/controllers/timeline_controller_test.exs
+++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs
@@ -445,6 +445,23 @@ defp ensure_authenticated_access(base_uri) do
       assert length(json_response(res_conn, 200)) == 2
     end
 
+    test "with default settings on private instances, returns 403 for unauthenticated users", %{
+      conn: conn,
+      base_uri: base_uri,
+      error_response: error_response
+    } do
+      clear_config([:instance, :public], false)
+      clear_config([:restrict_unauthenticated, :timelines])
+
+      for local <- [true, false] do
+        res_conn = get(conn, "#{base_uri}?local=#{local}")
+
+        assert json_response(res_conn, :unauthorized) == error_response
+      end
+
+      ensure_authenticated_access(base_uri)
+    end
+
     test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{
       conn: conn,
       base_uri: base_uri,