From 1bb4d5d65be725f374e06da88a5e8e826660596b Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 29 Mar 2019 21:59:04 +0300 Subject: [PATCH 01/26] Implement fake status submit --- lib/pleroma/web/activity_pub/activity_pub.ex | 31 ++++++++++---- lib/pleroma/web/common_api/common_api.ex | 17 ++++---- .../mastodon_api_controller_test.exs | 42 +++++++++++++++++++ 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 6e1ed7ec9..b459fd882 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -113,15 +113,15 @@ def decrease_replies_count_if_reply(%Object{ def decrease_replies_count_if_reply(_object), do: :noop - def insert(map, local \\ true) when is_map(map) do + def insert(map, local \\ true, fake \\ false) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map), :ok <- check_actor_is_active(map["actor"]), {_, true} <- {:remote_limit_error, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), + {recipients, _, _} = get_recipients(map), + {:fake, false, map, recipients} <- {:fake, fake, map, recipients}, {:ok, object} <- insert_full_object(map) do - {recipients, _, _} = get_recipients(map) - {:ok, activity} = Repo.insert(%Activity{ data: map, @@ -146,8 +146,21 @@ def insert(map, local \\ true) when is_map(map) do stream_out(activity) {:ok, activity} else - %Activity{} = activity -> {:ok, activity} - error -> {:error, error} + %Activity{} = activity -> + {:ok, activity} + + {:fake, true, map, recipients} -> + {:ok, + %Activity{ + data: map, + local: local, + actor: map["actor"], + recipients: recipients, + id: "pleroma:fakeid" + }} + + error -> + {:error, error} end end @@ -190,7 +203,7 @@ def stream_out(activity) do end end - def create(%{to: to, actor: actor, context: context, object: object} = params) do + def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do additional = params[:additional] || %{} # only accept false as false value local = !(params[:local] == false) @@ -201,13 +214,17 @@ def create(%{to: to, actor: actor, context: context, object: object} = params) d %{to: to, actor: actor, published: published, context: context, object: object}, additional ), - {:ok, activity} <- insert(create_data, local), + {:ok, activity} <- insert(create_data, local, fake), + {:fake, false, activity} <- {:fake, fake, activity}, _ <- increase_replies_count_if_reply(create_data), # Changing note count prior to enqueuing federation task in order to avoid # race conditions on updating user.info {:ok, _actor} <- increase_note_count_if_public(actor, activity), :ok <- maybe_federate(activity) do {:ok, activity} + else + {:fake, true, activity} -> + {:ok, activity} end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 25b990677..8e2937ac5 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -172,13 +172,16 @@ def post(user, %{"status" => status} = data) do end) ) do res = - ActivityPub.create(%{ - to: to, - actor: user, - context: context, - object: object, - additional: %{"cc" => cc, "directMessage" => visibility == "direct"} - }) + ActivityPub.create( + %{ + to: to, + actor: user, + context: context, + object: object, + additional: %{"cc" => cc, "directMessage" => visibility == "direct"} + }, + data["fake"] || false + ) res end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index d9bcbf5a9..3395c3689 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -143,6 +143,48 @@ test "posting a sensitive status", %{conn: conn} do assert Repo.get(Activity, id) end + test "posting a fake status", %{conn: conn} do + user = insert(:user) + + real_conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => + "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it" + }) + + real_status = + json_response(real_conn, 200) + |> Map.put("id", nil) + |> Map.put("url", nil) + |> Map.put("uri", nil) + |> Map.put("created_at", nil) + |> Kernel.put_in(["pleroma", "conversation_id"], nil) + + assert real_status + + fake_conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => + "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it", + "fake" => true + }) + + fake_status = + json_response(fake_conn, 200) + |> Map.put("id", nil) + |> Map.put("url", nil) + |> Map.put("uri", nil) + |> Map.put("created_at", nil) + |> Kernel.put_in(["pleroma", "conversation_id"], nil) + + assert fake_status + assert real_status == fake_status + end + test "posting a status with OGP link preview", %{conn: conn} do Pleroma.Config.put([:rich_media, :enabled], true) user = insert(:user) From 42b779527c551595399771fbc3c0701d38a3ed3d Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 29 Mar 2019 22:15:20 +0300 Subject: [PATCH 02/26] document fake option --- docs/api/differences_in_mastoapi_responses.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md index d993d1383..f5ce7493d 100644 --- a/docs/api/differences_in_mastoapi_responses.md +++ b/docs/api/differences_in_mastoapi_responses.md @@ -44,3 +44,9 @@ Has these additional fields under the `pleroma` object: Has these additional fields under the `pleroma` object: - `is_seen`: true if the notification was read by the user + +## POST `/api/v1/statuses` + +Additional parameters can be added to the JSON body: + +- `fake`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example. From cd387f8693c57b925576ab92f8202ef28007cfc0 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 30 Mar 2019 13:57:54 +0300 Subject: [PATCH 03/26] Add a fake option to lazy_put_actvity_defaults --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- lib/pleroma/web/activity_pub/utils.ex | 30 +++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index b459fd882..a94040d01 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -115,7 +115,7 @@ def decrease_replies_count_if_reply(_object), do: :noop def insert(map, local \\ true, fake \\ false) when is_map(map) do with nil <- Activity.normalize(map), - map <- lazy_put_activity_defaults(map), + map <- lazy_put_activity_defaults(map, fake), :ok <- check_actor_is_active(map["actor"]), {_, true} <- {:remote_limit_error, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 2e9ffe41c..3959e9bd9 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -175,21 +175,29 @@ def maybe_federate(_), do: :ok Adds an id and a published data if they aren't there, also adds it to an included object """ - def lazy_put_activity_defaults(map) do - %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) + def lazy_put_activity_defaults(map, fake \\ false) do + unless fake do + %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) - map = - map - |> Map.put_new_lazy("id", &generate_activity_id/0) - |> Map.put_new_lazy("published", &make_date/0) - |> Map.put_new("context", context) - |> Map.put_new("context_id", context_id) + map = + map + |> Map.put_new_lazy("id", &generate_activity_id/0) + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("context", context) + |> Map.put_new("context_id", context_id) - if is_map(map["object"]) do - object = lazy_put_object_defaults(map["object"], map) - %{map | "object" => object} + if is_map(map["object"]) do + object = lazy_put_object_defaults(map["object"], map) + %{map | "object" => object} + else + map + end else map + |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("context", "pleroma:fakecontext") + |> Map.put_new("context_id", -1) end end From 45ba10bf47baf350fd4d538cbe32cec447d496e6 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 11:55:59 +0300 Subject: [PATCH 04/26] Fix the issue with HTML scrubber --- lib/pleroma/html.ex | 17 +++++++++++++-- lib/pleroma/object.ex | 5 +++++ lib/pleroma/web/activity_pub/activity_pub.ex | 22 +++++++++++++------- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 5b152d926..f19b42b42 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -28,9 +28,13 @@ def filter_tags(html, scrubber), do: Scrubber.scrub(html, scrubber) def filter_tags(html), do: filter_tags(html, nil) def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags) + # TODO: rename object to activity because that's what it is really working with def get_cached_scrubbed_html_for_object(content, scrubbers, object, module) do key = "#{module}#{generate_scrubber_signature(scrubbers)}|#{object.id}" - Cachex.fetch!(:scrubber_cache, key, fn _key -> ensure_scrubbed_html(content, scrubbers) end) + + Cachex.fetch!(:scrubber_cache, key, fn _key -> + ensure_scrubbed_html(content, scrubbers, object.data["object"]["fake"] || false) + end) end def get_cached_stripped_html_for_object(content, object, module) do @@ -44,11 +48,20 @@ def get_cached_stripped_html_for_object(content, object, module) do def ensure_scrubbed_html( content, - scrubbers + scrubbers, + _fake = false ) do {:commit, filter_tags(content, scrubbers)} end + def ensure_scrubbed_html( + content, + scrubbers, + _fake = true + ) do + {:ignore, filter_tags(content, scrubbers)} + end + defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do generate_scrubber_signature([scrubber]) end diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 8a670645d..013d62157 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -44,6 +44,11 @@ def get_by_ap_id(ap_id) do # Use this whenever possible, especially when walking graphs in an O(N) loop! def normalize(%Activity{object: %Object{} = object}), do: object + # A hack for fake activities + def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}) do + %Object{id: "pleroma:fake_object_id", data: data} + end + # Catch and log Object.normalize() calls where the Activity's child object is not # preloaded. def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index a94040d01..716a40419 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -150,14 +150,20 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do {:ok, activity} {:fake, true, map, recipients} -> - {:ok, - %Activity{ - data: map, - local: local, - actor: map["actor"], - recipients: recipients, - id: "pleroma:fakeid" - }} + map = + map + |> put_in(["object", "fake"], true) + + activity = %Activity{ + data: map, + local: local, + actor: map["actor"], + recipients: recipients, + id: "pleroma:fakeid" + } + + # Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + {:ok, activity} error -> {:error, error} From d866b59eeaed25a1ad19581bd6c942f9f2d2711e Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 11:58:08 +0300 Subject: [PATCH 05/26] oof --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 716a40419..9cb4a0542 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -162,7 +162,7 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do id: "pleroma:fakeid" } - # Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) {:ok, activity} error -> From 975482f091f2f957c138d1b4f2d37e6b5d2b82a8 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 12:16:51 +0300 Subject: [PATCH 06/26] insert object defaults for fake activities and make credo happy --- lib/pleroma/html.ex | 4 ++-- lib/pleroma/web/activity_pub/utils.ex | 34 +++++++++++++++++---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index f19b42b42..1e48749a8 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -49,7 +49,7 @@ def get_cached_stripped_html_for_object(content, object, module) do def ensure_scrubbed_html( content, scrubbers, - _fake = false + false = _fake ) do {:commit, filter_tags(content, scrubbers)} end @@ -57,7 +57,7 @@ def ensure_scrubbed_html( def ensure_scrubbed_html( content, scrubbers, - _fake = true + true = _fake ) do {:ignore, filter_tags(content, scrubbers)} end diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 3959e9bd9..feb73518e 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -176,35 +176,45 @@ def maybe_federate(_), do: :ok also adds it to an included object """ def lazy_put_activity_defaults(map, fake \\ false) do - unless fake do - %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) + map = + unless fake do + %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) - map = map |> Map.put_new_lazy("id", &generate_activity_id/0) |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", context) |> Map.put_new("context_id", context_id) - - if is_map(map["object"]) do - object = lazy_put_object_defaults(map["object"], map) - %{map | "object" => object} else map + |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("context", "pleroma:fakecontext") + |> Map.put_new("context_id", -1) end + + if is_map(map["object"]) do + object = lazy_put_object_defaults(map["object"], map, fake) + %{map | "object" => object} else map - |> Map.put_new("id", "pleroma:fakeid") - |> Map.put_new_lazy("published", &make_date/0) - |> Map.put_new("context", "pleroma:fakecontext") - |> Map.put_new("context_id", -1) end end @doc """ Adds an id and published date if they aren't there. """ - def lazy_put_object_defaults(map, activity \\ %{}) do + def lazy_put_object_defaults(map, activity \\ %{}, fake) + + def lazy_put_object_defaults(map, activity, true = _fake) do + map + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new("context", activity["context"]) + |> Map.put_new("context_id", activity["context_id"]) + end + + def lazy_put_object_defaults(map, activity, _fake) do map |> Map.put_new_lazy("id", &generate_object_id/0) |> Map.put_new_lazy("published", &make_date/0) From fe5145eeaab81573614a3475463a24229a6a58a3 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 12:25:53 +0300 Subject: [PATCH 07/26] Move putting fake attribute to lib/pleroma/web/activity_pub/utils.ex --- lib/pleroma/web/activity_pub/activity_pub.ex | 4 ---- lib/pleroma/web/activity_pub/utils.ex | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9cb4a0542..f217e7bac 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -150,10 +150,6 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do {:ok, activity} {:fake, true, map, recipients} -> - map = - map - |> put_in(["object", "fake"], true) - activity = %Activity{ data: map, local: local, diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index feb73518e..d22da6f40 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -209,8 +209,9 @@ def lazy_put_object_defaults(map, activity \\ %{}, fake) def lazy_put_object_defaults(map, activity, true = _fake) do map |> Map.put_new_lazy("published", &make_date/0) - |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new("id", "pleroma:fake_object_id") |> Map.put_new("context", activity["context"]) + |> Map.put_new("fake", true) |> Map.put_new("context_id", activity["context_id"]) end From b6f9f7b8aa659c10049b8c43326e58a4b1b18664 Mon Sep 17 00:00:00 2001 From: Sergey Suprunenko Date: Mon, 1 Apr 2019 22:40:48 +0200 Subject: [PATCH 08/26] Handle dates in the Unix timestamp format (Fixes #763) --- lib/pleroma/web/common_api/utils.ex | 17 ++++++++- test/web/common_api/common_api_utils_test.exs | 37 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index f596f703b..3f5348d66 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -240,8 +240,23 @@ def format_asctime(date) do Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y") end + def date_to_asctime(date) when is_float(date) do + date + |> trunc() + |> date_to_asctime() + end + + def date_to_asctime(date) when is_integer(date) do + with {:ok, date} <- DateTime.from_unix(date) do + format_asctime(date) + else + _e -> + "" + end + end + def date_to_asctime(date) do - with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do + with {:ok, date, _offset} <- DateTime.from_iso8601(date) do format_asctime(date) else _e -> diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index e04b9f9b5..0f8b28d9c 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -153,4 +153,41 @@ test "returns an existing mapping for an existing object" do assert conversation_id == object.id end end + + describe "formats date to asctime" do + test "when date is an integer Unix timestamp" do + date = DateTime.utc_now() |> DateTime.to_unix() + + expected = + date + |> DateTime.from_unix!() + |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y") + + assert Utils.date_to_asctime(date) == expected + end + + test "when date is a float Unix timestamp" do + date = 1_553_808_404.602961 + + expected = + date + |> trunc() + |> DateTime.from_unix!() + |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y") + + assert Utils.date_to_asctime(date) == expected + end + + test "when date is in ISO 8601 format" do + date = DateTime.utc_now() |> DateTime.to_iso8601() + + expected = + date + |> DateTime.from_iso8601() + |> elem(1) + |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y") + + assert Utils.date_to_asctime(date) == expected + end + end end From 3db923515057b7da23e4bb58a1696cd14df7ed52 Mon Sep 17 00:00:00 2001 From: Sergey Suprunenko Date: Tue, 2 Apr 2019 11:25:51 +0200 Subject: [PATCH 09/26] Ignore dates in wrong formats --- lib/pleroma/web/common_api/utils.ex | 21 +++------ test/web/common_api/common_api_utils_test.exs | 45 +++++++++---------- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 3f5348d66..0bf4de2f6 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -15,6 +15,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do alias Pleroma.Web.Endpoint alias Pleroma.Web.MediaProxy + require Logger + # This is a hack for twidere. def get_by_id_or_ap_id(id) do activity = @@ -240,28 +242,19 @@ def format_asctime(date) do Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y") end - def date_to_asctime(date) when is_float(date) do - date - |> trunc() - |> date_to_asctime() - end - - def date_to_asctime(date) when is_integer(date) do - with {:ok, date} <- DateTime.from_unix(date) do + def date_to_asctime(date) when is_binary(date) do + with {:ok, date, _offset} <- DateTime.from_iso8601(date) do format_asctime(date) else _e -> + Logger.warn("Date #{date} in wrong format, must be ISO 8601") "" end end def date_to_asctime(date) do - with {:ok, date, _offset} <- DateTime.from_iso8601(date) do - format_asctime(date) - else - _e -> - "" - end + Logger.warn("Date #{date} in wrong format, must be ISO 8601") + "" end def to_masto_date(%NaiveDateTime{} = date) do diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index 0f8b28d9c..f0c59d5c3 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -155,29 +155,6 @@ test "returns an existing mapping for an existing object" do end describe "formats date to asctime" do - test "when date is an integer Unix timestamp" do - date = DateTime.utc_now() |> DateTime.to_unix() - - expected = - date - |> DateTime.from_unix!() - |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y") - - assert Utils.date_to_asctime(date) == expected - end - - test "when date is a float Unix timestamp" do - date = 1_553_808_404.602961 - - expected = - date - |> trunc() - |> DateTime.from_unix!() - |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y") - - assert Utils.date_to_asctime(date) == expected - end - test "when date is in ISO 8601 format" do date = DateTime.utc_now() |> DateTime.to_iso8601() @@ -189,5 +166,27 @@ test "when date is in ISO 8601 format" do assert Utils.date_to_asctime(date) == expected end + + test "when date is a binary in wrong format" do + date = DateTime.utc_now() + + expected = "" + + assert Utils.date_to_asctime(date) == expected + end + + test "when date is a Unix timestamp" do + date = DateTime.utc_now() |> DateTime.to_unix() + + expected = "" + + assert Utils.date_to_asctime(date) == expected + end + + test "when date is nil" do + expected = "" + + assert Utils.date_to_asctime(nil) == expected + end end end From fdb4357e9ba7a34a603997d50d85593ca2bf6395 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 2 Apr 2019 14:31:18 +0300 Subject: [PATCH 10/26] Rename fake param to preview and make the tests check that the object was not inserted to the db --- docs/api/differences_in_mastoapi_responses.md | 2 +- lib/pleroma/web/common_api/common_api.ex | 2 +- .../mastodon_api_controller_test.exs | 19 +++++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md index f5ce7493d..7adf29676 100644 --- a/docs/api/differences_in_mastoapi_responses.md +++ b/docs/api/differences_in_mastoapi_responses.md @@ -49,4 +49,4 @@ Has these additional fields under the `pleroma` object: Additional parameters can be added to the JSON body: -- `fake`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example. +- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example. diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 8e2937ac5..2f82a32f3 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -180,7 +180,7 @@ def post(user, %{"status" => status} = data) do object: object, additional: %{"cc" => cc, "directMessage" => visibility == "direct"} }, - data["fake"] || false + data["preview"] || false ) res diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 3395c3689..d17d58962 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -154,34 +154,41 @@ test "posting a fake status", %{conn: conn} do "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it" }) + real_status = json_response(real_conn, 200) + + assert real_status + assert Object.get_by_ap_id(real_status["uri"]) + real_status = - json_response(real_conn, 200) + real_status |> Map.put("id", nil) |> Map.put("url", nil) |> Map.put("uri", nil) |> Map.put("created_at", nil) |> Kernel.put_in(["pleroma", "conversation_id"], nil) - assert real_status - fake_conn = conn |> assign(:user, user) |> post("/api/v1/statuses", %{ "status" => "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it", - "fake" => true + "preview" => true }) + fake_status = json_response(fake_conn, 200) + + assert fake_status + refute Object.get_by_ap_id(fake_status["uri"]) + fake_status = - json_response(fake_conn, 200) + fake_status |> Map.put("id", nil) |> Map.put("url", nil) |> Map.put("uri", nil) |> Map.put("created_at", nil) |> Kernel.put_in(["pleroma", "conversation_id"], nil) - assert fake_status assert real_status == fake_status end From fd07745d1b18e2a1eeb88a99eaa9d5e728d1aa71 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 2 Apr 2019 16:04:18 +0200 Subject: [PATCH 11/26] ActivityPub Utils: Greatly speed up the follow / block activity fetching. --- lib/pleroma/web/activity_pub/utils.ex | 16 ++++++++------ test/web/activity_pub/activity_pub_test.exs | 10 --------- test/web/activity_pub/utils_test.exs | 24 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 2e9ffe41c..a4b1518de 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -404,13 +404,15 @@ def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do activity.data ), where: activity.actor == ^follower_id, + # this is to use the index where: fragment( - "? @> ?", + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, - ^%{object: followed_id} + activity.data, + ^followed_id ), - order_by: [desc: :id], + order_by: [fragment("? desc nulls last", activity.id)], limit: 1 ) @@ -567,13 +569,15 @@ def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do activity.data ), where: activity.actor == ^blocker_id, + # this is to use the index where: fragment( - "? @> ?", + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, - ^%{object: blocked_id} + activity.data, + ^blocked_id ), - order_by: [desc: :id], + order_by: [fragment("? desc nulls last", activity.id)], limit: 1 ) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index ac5fbe0a9..5ff157e93 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -635,16 +635,6 @@ test "works with base64 encoded images" do end end - describe "fetch the latest Follow" do - test "fetches the latest Follow activity" do - %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity) - follower = Repo.get_by(User, ap_id: activity.data["actor"]) - followed = Repo.get_by(User, ap_id: activity.data["object"]) - - assert activity == Utils.fetch_latest_follow(follower, followed) - end - end - describe "fetching an object" do test "it fetches an object" do {:ok, object} = diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index 2bd3ddf93..6b9961d82 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -1,10 +1,34 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do use Pleroma.DataCase + alias Pleroma.Activity + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI import Pleroma.Factory + describe "fetch the latest Follow" do + test "fetches the latest Follow activity" do + %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity) + follower = Repo.get_by(User, ap_id: activity.data["actor"]) + followed = Repo.get_by(User, ap_id: activity.data["object"]) + + assert activity == Utils.fetch_latest_follow(follower, followed) + end + end + + describe "fetch the latest Block" do + test "fetches the latest Block activity" do + blocker = insert(:user) + blocked = insert(:user) + {:ok, activity} = ActivityPub.block(blocker, blocked) + + assert activity == Utils.fetch_latest_block(blocker, blocked) + end + end + describe "determine_explicit_mentions()" do test "works with an object that has mentions" do object = %{ From 79cb34a4b0dd1c0ffe45e796f5ac6790e3b31025 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 2 Apr 2019 23:07:16 +0300 Subject: [PATCH 12/26] Fix preview not being usable in form data --- docs/api/differences_in_mastoapi_responses.md | 2 +- lib/pleroma/web/common_api/common_api.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md index 7adf29676..215f43155 100644 --- a/docs/api/differences_in_mastoapi_responses.md +++ b/docs/api/differences_in_mastoapi_responses.md @@ -47,6 +47,6 @@ Has these additional fields under the `pleroma` object: ## POST `/api/v1/statuses` -Additional parameters can be added to the JSON body: +Additional parameters can be added to the JSON body/Form data: - `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example. diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 2f82a32f3..745d1839b 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -180,7 +180,7 @@ def post(user, %{"status" => status} = data) do object: object, additional: %{"cc" => cc, "directMessage" => visibility == "direct"} }, - data["preview"] || false + Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false ) res From d313a0c49c1c42bff60a34b088145e700eb5db53 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 3 Apr 2019 19:41:15 +0700 Subject: [PATCH 13/26] Update `auto_linker` dependency --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 333f21a91..88da6332a 100644 --- a/mix.exs +++ b/mix.exs @@ -93,7 +93,7 @@ defp deps do {:timex, "~> 3.5"}, {:auto_linker, git: "https://git.pleroma.social/pleroma/auto_linker.git", - ref: "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd"}, + ref: "479dd343f4e563ff91215c8275f3b5c67e032850"}, {:pleroma_job_queue, "~> 0.2.0"} ] end diff --git a/mix.lock b/mix.lock index f401258e9..9c454446a 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd", [ref: "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd"]}, + "auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "479dd343f4e563ff91215c8275f3b5c67e032850", [ref: "479dd343f4e563ff91215c8275f3b5c67e032850"]}, "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, "cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"}, From 86f90c0a54d8f0ee6e6a787e8d28ebc53ff13b05 Mon Sep 17 00:00:00 2001 From: Alex S Date: Wed, 3 Apr 2019 20:51:09 +0700 Subject: [PATCH 14/26] adding indexes to oauth_tokens table --- .../20190403131720_add_oauth_token_indexes.exs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs diff --git a/priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs b/priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs new file mode 100644 index 000000000..ebcd29389 --- /dev/null +++ b/priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddOauthTokenIndexes do + use Ecto.Migration + + def change do + create(unique_index(:oauth_tokens, [:token])) + create(index(:oauth_tokens, [:app_id])) + create(index(:oauth_tokens, [:user_id])) + end +end From cd41584ac4a90a666f33786d967d37a619c67762 Mon Sep 17 00:00:00 2001 From: Sachin Joshi Date: Wed, 3 Apr 2019 00:54:16 +0545 Subject: [PATCH 15/26] Generate permissive or restrictive robots.txt in the config generator --- lib/mix/tasks/pleroma/instance.ex | 34 ++++++++++++++++++++++++++++ lib/mix/tasks/pleroma/robots_txt.eex | 2 ++ 2 files changed, 36 insertions(+) create mode 100644 lib/mix/tasks/pleroma/robots_txt.eex diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 1ba452275..8f8d86a11 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -81,6 +81,14 @@ def run(["gen" | rest]) do email = Common.get_option(options, :admin_email, "What is your admin email address?") + indexable = + Common.get_option( + options, + :indexable, + "Do you want search engines to index your site? (y/n)", + "y" + ) === "y" + dbhost = Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost") @@ -142,6 +150,8 @@ def run(["gen" | rest]) do Mix.shell().info("Writing #{psql_path}.") File.write(psql_path, result_psql) + write_robots_txt(indexable) + Mix.shell().info( "\n" <> """ @@ -163,4 +173,28 @@ def run(["gen" | rest]) do ) end end + + defp write_robots_txt(indexable) do + robots_txt = + EEx.eval_file( + Path.expand("robots_txt.eex", __DIR__), + indexable: indexable + ) + + static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/") + + unless File.exists?(static_dir) do + File.mkdir_p!(static_dir) + end + + robots_txt_path = Path.join(static_dir, "robots.txt") + + if File.exists?(robots_txt_path) do + File.cp!(robots_txt_path, "#{robots_txt_path}.bak") + Mix.shell().info("Backing up existing robots.txt to #{robots_txt_path}.bak") + end + + File.write(robots_txt_path, robots_txt) + Mix.shell().info("Writing #{robots_txt_path}.") + end end diff --git a/lib/mix/tasks/pleroma/robots_txt.eex b/lib/mix/tasks/pleroma/robots_txt.eex new file mode 100644 index 000000000..1af3c47ee --- /dev/null +++ b/lib/mix/tasks/pleroma/robots_txt.eex @@ -0,0 +1,2 @@ +User-Agent: * +Disallow: <%= if indexable, do: "", else: "/" %> From af0065a71feb470bd69bec36999bc40a662e3e83 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 4 Apr 2019 09:07:25 +0200 Subject: [PATCH 16/26] mastodon_api_controller.ex: Add pleroma-tan to initial_state --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 0de2cca4e..89fd7629a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1121,7 +1121,8 @@ def index(%{assigns: %{user: user}} = conn, _params) do auto_play_gif: false, display_sensitive_media: false, reduce_motion: false, - max_toot_chars: limit + max_toot_chars: limit, + mascot: "/images/pleroma-fox-tan-smol.png" }, rights: %{ delete_others_notice: present?(user.info.is_moderator), From 3b12eeda192e739e8328ef4202059bb482d1cff2 Mon Sep 17 00:00:00 2001 From: feld Date: Thu, 4 Apr 2019 19:52:22 +0000 Subject: [PATCH 17/26] Add ability to ship logs to a Slack channel --- config/config.exs | 5 +++++ docs/config.md | 20 +++++++++++++++++++- mix.exs | 5 +++-- mix.lock | 1 + 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/config/config.exs b/config/config.exs index dccf7b263..c143f79fc 100644 --- a/config/config.exs +++ b/config/config.exs @@ -118,6 +118,11 @@ format: "$metadata[$level] $message", metadata: [:request_id] +config :quack, + level: :warn, + meta: [:all], + webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" + config :mime, :types, %{ "application/xml" => ["xml"], "application/xrd+xml" => ["xrd+xml"], diff --git a/docs/config.md b/docs/config.md index 97a0e6ffa..06d6fd757 100644 --- a/docs/config.md +++ b/docs/config.md @@ -105,7 +105,7 @@ config :pleroma, Pleroma.Mailer, * `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`) ## :logger -* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog +* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed: ``` @@ -128,6 +128,24 @@ config :logger, :ex_syslogger, See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/) +An example of logging info to local syslog, but warn to a Slack channel: +``` +config :logger, + backends: [ {ExSyslogger, :ex_syslogger}, Quack.Logger ], + level: :info + +config :logger, :ex_syslogger, + level: :info, + ident: "pleroma", + format: "$metadata[$level] $message" + +config :quack, + level: :warn, + meta: [:all], + webhook_url: "https://hooks.slack.com/services/YOUR-API-KEY-HERE" +``` + +See the [Quack Github](https://github.com/azohra/quack) for more details ## :frontend_configurations diff --git a/mix.exs b/mix.exs index 88da6332a..3a04e0060 100644 --- a/mix.exs +++ b/mix.exs @@ -41,7 +41,7 @@ def project do def application do [ mod: {Pleroma.Application, []}, - extra_applications: [:logger, :runtime_tools, :comeonin], + extra_applications: [:logger, :runtime_tools, :comeonin, :quack], included_applications: [:ex_syslogger] ] end @@ -94,7 +94,8 @@ defp deps do {:auto_linker, git: "https://git.pleroma.social/pleroma/auto_linker.git", ref: "479dd343f4e563ff91215c8275f3b5c67e032850"}, - {:pleroma_job_queue, "~> 0.2.0"} + {:pleroma_job_queue, "~> 0.2.0"}, + {:quack, "~> 0.1.1"} ] end diff --git a/mix.lock b/mix.lock index 9c454446a..d84d11049 100644 --- a/mix.lock +++ b/mix.lock @@ -57,6 +57,7 @@ "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"}, "postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, + "quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, "swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"}, From f0f30019e1c9992cb420ba54457840cddaeb6a3a Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 5 Apr 2019 15:19:44 +0300 Subject: [PATCH 18/26] Refactor html caching functions to have a key instead of a module, use more correct terminology and fix summaries in mastoapi --- lib/pleroma/html.ex | 15 +++++++-------- lib/pleroma/web/mastodon_api/views/status_view.ex | 14 +++++++++++--- lib/pleroma/web/metadata/utils.ex | 2 +- .../web/twitter_api/views/activity_view.ex | 6 +++--- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 1e48749a8..7f1dbe28c 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -28,21 +28,20 @@ def filter_tags(html, scrubber), do: Scrubber.scrub(html, scrubber) def filter_tags(html), do: filter_tags(html, nil) def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags) - # TODO: rename object to activity because that's what it is really working with - def get_cached_scrubbed_html_for_object(content, scrubbers, object, module) do - key = "#{module}#{generate_scrubber_signature(scrubbers)}|#{object.id}" + def get_cached_scrubbed_html_for_activity(content, scrubbers, activity, key \\ "") do + key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}" Cachex.fetch!(:scrubber_cache, key, fn _key -> - ensure_scrubbed_html(content, scrubbers, object.data["object"]["fake"] || false) + ensure_scrubbed_html(content, scrubbers, activity.data["object"]["fake"] || false) end) end - def get_cached_stripped_html_for_object(content, object, module) do - get_cached_scrubbed_html_for_object( + def get_cached_stripped_html_for_activity(content, activity, key) do + get_cached_scrubbed_html_for_activity( content, HtmlSanitizeEx.Scrubber.StripTags, - object, - module + activity, + key ) end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 200bb453d..4c0b53bdd 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -147,10 +147,18 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity} content = object |> render_content() - |> HTML.get_cached_scrubbed_html_for_object( + |> HTML.get_cached_scrubbed_html_for_activity( User.html_filter_policy(opts[:for]), activity, - __MODULE__ + "mastoapi:content" + ) + + summary = + (object["summary"] || "") + |> HTML.get_cached_scrubbed_html_for_activity( + User.html_filter_policy(opts[:for]), + activity, + "mastoapi:summary" ) card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)) @@ -182,7 +190,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity} muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user), pinned: pinned?(activity, user), sensitive: sensitive, - spoiler_text: object["summary"] || "", + spoiler_text: summary, visibility: get_visibility(object), media_attachments: attachments, mentions: mentions, diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index 23bbde1a6..58385a3d1 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -12,7 +12,7 @@ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do # html content comes from DB already encoded, decode first and scrub after |> HtmlEntities.decode() |> String.replace(~r//, " ") - |> HTML.get_cached_stripped_html_for_object(object, __MODULE__) + |> HTML.get_cached_stripped_html_for_activity(object, "metadata") |> Formatter.demojify() |> Formatter.truncate() end diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index aa1d41fa2..433322eb8 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -254,10 +254,10 @@ def render( html = content - |> HTML.get_cached_scrubbed_html_for_object( + |> HTML.get_cached_scrubbed_html_for_activity( User.html_filter_policy(opts[:for]), activity, - __MODULE__ + "twitterapi:content" ) |> Formatter.emojify(object["emoji"]) @@ -265,7 +265,7 @@ def render( if content do content |> String.replace(~r//, "\n") - |> HTML.get_cached_stripped_html_for_object(activity, __MODULE__) + |> HTML.get_cached_stripped_html_for_activity(activity, "twitterapi:content") else "" end From 7895ee37fae82de26b3c06e69a96788d8c88d139 Mon Sep 17 00:00:00 2001 From: Roger Braun Date: Sun, 16 Dec 2018 16:41:56 +0100 Subject: [PATCH 19/26] Add user following / unfollowing to the admin api. --- .../web/admin_api/admin_api_controller.ex | 20 ++++++++ lib/pleroma/web/router.ex | 4 ++ .../admin_api/admin_api_controller_test.exs | 46 +++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index b3a09e49e..84d0aabaf 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -25,6 +25,26 @@ def user_delete(conn, %{"nickname" => nickname}) do |> json(nickname) end + def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do + with %User{} = follower <- Repo.get_by(User, %{nickname: follower_nick}), + %User{} = followed <- Repo.get_by(User, %{nickname: followed_nick}) do + User.follow(follower, followed) + end + + conn + |> json("ok") + end + + def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do + with %User{} = follower <- Repo.get_by(User, %{nickname: follower_nick}), + %User{} = followed <- Repo.get_by(User, %{nickname: followed_nick}) do + User.unfollow(follower, followed) + end + + conn + |> json("ok") + end + def user_create( conn, %{"nickname" => nickname, "email" => email, "password" => password} diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 605a327fc..1c752e44c 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -140,8 +140,12 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do pipe_through([:admin_api, :oauth_write]) + post("/user/follow", AdminAPIController, :user_follow) + post("/user/unfollow", AdminAPIController, :user_unfollow) + get("/users", AdminAPIController, :list_users) get("/users/:nickname", AdminAPIController, :user_show) + delete("/user", AdminAPIController, :user_delete) patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation) post("/user", AdminAPIController, :user_create) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index acae64361..cedc907ec 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -74,6 +74,52 @@ test "when the user doesn't exist", %{conn: conn} do end end + describe "/api/pleroma/admin/user/follow" do + test "allows to force-follow another user" do + admin = insert(:user, info: %{is_admin: true}) + user = insert(:user) + follower = insert(:user) + + conn = + build_conn() + |> assign(:user, admin) + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/user/follow", %{ + "follower" => follower.nickname, + "followed" => user.nickname + }) + + user = Repo.get(User, user.id) + follower = Repo.get(User, follower.id) + + assert User.following?(follower, user) + end + end + + describe "/api/pleroma/admin/user/unfollow" do + test "allows to force-unfollow another user" do + admin = insert(:user, info: %{is_admin: true}) + user = insert(:user) + follower = insert(:user) + + User.follow(follower, user) + + conn = + build_conn() + |> assign(:user, admin) + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/user/unfollow", %{ + "follower" => follower.nickname, + "followed" => user.nickname + }) + + user = Repo.get(User, user.id) + follower = Repo.get(User, follower.id) + + refute User.following?(follower, user) + end + end + describe "PUT /api/pleroma/admin/users/tag" do setup do admin = insert(:user, info: %{is_admin: true}) From da64a5aece131d6bd8c0d17dcda61c626b44c4d0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 5 Apr 2019 11:29:34 -0500 Subject: [PATCH 20/26] Document the admin API endpoints for controlling follow/unfollow --- docs/api/admin_api.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/api/admin_api.md b/docs/api/admin_api.md index 53b68ffd4..86cacebb1 100644 --- a/docs/api/admin_api.md +++ b/docs/api/admin_api.md @@ -58,6 +58,26 @@ Authentication is required and the user must be an admin. - `password` - Response: User’s nickname +## `/api/pleroma/admin/user/follow` +### Make a user follow another user + +- Methods: `POST` +- Params: + - `follower`: The nickname of the follower + - `followed`: The nickname of the followed +- Response: + - "ok" + +## `/api/pleroma/admin/user/unfollow` +### Make a user unfollow another user + +- Methods: `POST` +- Params: + - `follower`: The nickname of the follower + - `followed`: The nickname of the followed +- Response: + - "ok" + ## `/api/pleroma/admin/users/:nickname/toggle_activation` ### Toggle user activation From b5a2d384f71de9f7ff33d99c95c5db4674141d9a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 5 Apr 2019 11:41:41 -0500 Subject: [PATCH 21/26] Redundant Repo.get_by usage was recently removed from the codebase --- lib/pleroma/web/admin_api/admin_api_controller.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 84d0aabaf..78bf31893 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -26,8 +26,8 @@ def user_delete(conn, %{"nickname" => nickname}) do end def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do - with %User{} = follower <- Repo.get_by(User, %{nickname: follower_nick}), - %User{} = followed <- Repo.get_by(User, %{nickname: followed_nick}) do + with %User{} = follower <- User.get_by_nickname(follower_nick), + %User{} = followed <- User.get_by_nickname(followed_nick) do User.follow(follower, followed) end @@ -36,8 +36,8 @@ def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick end def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do - with %User{} = follower <- Repo.get_by(User, %{nickname: follower_nick}), - %User{} = followed <- Repo.get_by(User, %{nickname: followed_nick}) do + with %User{} = follower <- User.get_by_nickname(follower_nick), + %User{} = followed <- User.get_by_nickname(followed_nick) do User.unfollow(follower, followed) end From c746087f570e366976b9b89c2aa6c2a5ff83c9ca Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 5 Apr 2019 11:59:56 -0500 Subject: [PATCH 22/26] Also remove Repo functions in the tests --- test/web/admin_api/admin_api_controller_test.exs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index cedc907ec..9c1cae6b7 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -89,8 +89,8 @@ test "allows to force-follow another user" do "followed" => user.nickname }) - user = Repo.get(User, user.id) - follower = Repo.get(User, follower.id) + user = User.get_by_nickname(user.id) + follower = User.get_by_nickname(follower.id) assert User.following?(follower, user) end @@ -113,8 +113,8 @@ test "allows to force-unfollow another user" do "followed" => user.nickname }) - user = Repo.get(User, user.id) - follower = Repo.get(User, follower.id) + user = User.get_by_nickname(user.id) + follower = User.get_by_nickname(follower.id) refute User.following?(follower, user) end From fac76bfa35f735005249111e74ea6be8670f5755 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 5 Apr 2019 12:11:19 -0500 Subject: [PATCH 23/26] We actually want the user id not nickname in the test... --- test/web/admin_api/admin_api_controller_test.exs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 9c1cae6b7..dd2fbfb15 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -89,8 +89,8 @@ test "allows to force-follow another user" do "followed" => user.nickname }) - user = User.get_by_nickname(user.id) - follower = User.get_by_nickname(follower.id) + user = User.get_by_id(user.id) + follower = User.get_by_id(follower.id) assert User.following?(follower, user) end @@ -113,8 +113,8 @@ test "allows to force-unfollow another user" do "followed" => user.nickname }) - user = User.get_by_nickname(user.id) - follower = User.get_by_nickname(follower.id) + user = User.get_by_id(user.id) + follower = User.get_by_id(follower.id) refute User.following?(follower, user) end From fb1be1d79892a72b10af2c24479e81600603a6af Mon Sep 17 00:00:00 2001 From: optikfluffel Date: Fri, 5 Apr 2019 20:12:44 +0200 Subject: [PATCH 24/26] Use --cover option when running CI tests --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c07f1a5d3..0bd657d67 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,7 +50,7 @@ unit-testing: script: - mix ecto.create - mix ecto.migrate - - mix test --trace --preload-modules + - mix test --trace --preload-modules --cover lint: stage: test From e5df8cadeaa1ee3992e31e6ac00a0c391da7e4bd Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 5 Apr 2019 19:59:03 +0000 Subject: [PATCH 25/26] Revert "Merge branch 'test-coverage' into 'develop'" This reverts merge request !1027 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0bd657d67..c07f1a5d3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,7 +50,7 @@ unit-testing: script: - mix ecto.create - mix ecto.migrate - - mix test --trace --preload-modules --cover + - mix test --trace --preload-modules lint: stage: test From 325a2680173f714a5875ed726f9171e7984f7f7a Mon Sep 17 00:00:00 2001 From: Sergey Suprunenko Date: Fri, 5 Apr 2019 23:36:42 +0000 Subject: [PATCH 26/26] Redirect to the referer url after mastofe authorization --- .../mastodon_api/mastodon_api_controller.ex | 19 ++++-- test/support/factory.ex | 10 +++ .../mastodon_api_controller_test.exs | 67 +++++++++++++++++++ 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 89fd7629a..bcc79b08a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1091,9 +1091,7 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) end def index(%{assigns: %{user: user}} = conn, _params) do - token = - conn - |> get_session(:oauth_token) + token = get_session(conn, :oauth_token) if user && token do mastodon_emoji = mastodonized_emoji() @@ -1194,6 +1192,7 @@ def index(%{assigns: %{user: user}} = conn, _params) do |> render("index.html", %{initial_state: initial_state, flavour: flavour}) else conn + |> put_session(:return_to, conn.request_path) |> redirect(to: "/web/login") end end @@ -1278,12 +1277,20 @@ def login(conn, _) do scope: Enum.join(app.scopes, " ") ) - conn - |> redirect(to: path) + redirect(conn, to: path) end end - defp local_mastodon_root_path(conn), do: mastodon_api_path(conn, :index, ["getting-started"]) + defp local_mastodon_root_path(conn) do + case get_session(conn, :return_to) do + nil -> + mastodon_api_path(conn, :index, ["getting-started"]) + + return_to -> + delete_session(conn, :return_to) + return_to + end + end defp get_or_make_app do find_attrs = %{client_name: @local_mastodon_name, redirect_uris: "."} diff --git a/test/support/factory.ex b/test/support/factory.ex index e1a08315a..b37bc2c07 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -240,6 +240,16 @@ def oauth_token_factory do } end + def oauth_authorization_factory do + %Pleroma.Web.OAuth.Authorization{ + token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false), + scopes: ["read", "write", "follow", "push"], + valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10), + user: build(:user), + app: build(:oauth_app) + } + end + def push_subscription_factory do %Pleroma.Web.Push.Subscription{ user: build(:user), diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 6060cc97f..438e9507d 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -2340,4 +2340,71 @@ test "accounts fetches correct account for nicknames beginning with numbers", %{ refute acc_one == acc_two assert acc_two == acc_three end + + describe "index/2 redirections" do + setup %{conn: conn} do + session_opts = [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + + conn = + conn + |> Plug.Session.call(Plug.Session.init(session_opts)) + |> fetch_session() + + test_path = "/web/statuses/test" + %{conn: conn, path: test_path} + end + + test "redirects not logged-in users to the login page", %{conn: conn, path: path} do + conn = get(conn, path) + + assert conn.status == 302 + assert redirected_to(conn) == "/web/login" + end + + test "does not redirect logged in users to the login page", %{conn: conn, path: path} do + token = insert(:oauth_token) + + conn = + conn + |> assign(:user, token.user) + |> put_session(:oauth_token, token.token) + |> get(path) + + assert conn.status == 200 + end + + test "saves referer path to session", %{conn: conn, path: path} do + conn = get(conn, path) + return_to = Plug.Conn.get_session(conn, :return_to) + + assert return_to == path + end + + test "redirects to the saved path after log in", %{conn: conn, path: path} do + app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".") + auth = insert(:oauth_authorization, app: app) + + conn = + conn + |> put_session(:return_to, path) + |> get("/web/login", %{code: auth.token}) + + assert conn.status == 302 + assert redirected_to(conn) == path + end + + test "redirects to the getting-started page when referer is not present", %{conn: conn} do + app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".") + auth = insert(:oauth_authorization, app: app) + + conn = get(conn, "/web/login", %{code: auth.token}) + + assert conn.status == 302 + assert redirected_to(conn) == "/web/getting-started" + end + end end