From 67b537f380f472fad481128733b62b8713596b9b Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 21 Apr 2021 18:57:23 +0200 Subject: [PATCH 01/31] Fix sentry issues Signed-off-by: Thomas Citharel --- lib/federation/activity_pub/activity_pub.ex | 93 +++++++++++-------- lib/federation/activity_pub/federator.ex | 2 +- lib/federation/activity_pub/fetcher.ex | 18 +++- lib/federation/activity_pub/preloader.ex | 1 + lib/federation/activity_pub/refresher.ex | 41 ++++++-- lib/federation/activity_pub/transmogrifier.ex | 50 ++++++++-- lib/federation/activity_pub/utils.ex | 41 +++++++- lib/federation/http_signatures/signature.ex | 19 ++-- lib/mobilizon/actors/actors.ex | 21 ++++- .../controllers/activity_pub_controller.ex | 21 ++++- lib/web/views/error_view.ex | 4 + 11 files changed, 229 insertions(+), 82 deletions(-) diff --git a/lib/federation/activity_pub/activity_pub.ex b/lib/federation/activity_pub/activity_pub.ex index 824b9c3c5..08cd8e825 100644 --- a/lib/federation/activity_pub/activity_pub.ex +++ b/lib/federation/activity_pub/activity_pub.ex @@ -79,7 +79,6 @@ defmodule Mobilizon.Federation.ActivityPub do {:ok, struct()} | {:error, any()} def fetch_object_from_url(url, options \\ []) do Logger.info("Fetching object from url #{url}") - force_fetch = Keyword.get(options, :force, false) with {:not_http, true} <- {:not_http, String.starts_with?(url, "http")}, {:existing, nil} <- @@ -99,39 +98,7 @@ defmodule Mobilizon.Federation.ActivityPub do Preloader.maybe_preload(entity) else {:existing, entity} -> - Logger.debug("Entity is already existing") - - res = - if force_fetch and not are_same_origin?(url, Endpoint.url()) do - Logger.debug("Entity is external and we want a force fetch") - - case Fetcher.fetch_and_update(url, options) do - {:ok, _activity, entity} -> - {:ok, entity} - - {:error, "Gone"} -> - {:error, "Gone", entity} - - {:error, "Not found"} -> - {:error, "Not found", entity} - end - else - {:ok, entity} - end - - Logger.debug("Going to preload an existing entity") - - case res do - {:ok, entity} -> - Preloader.maybe_preload(entity) - - {:error, status, entity} -> - {:ok, entity} = Preloader.maybe_preload(entity) - {:error, status, entity} - - err -> - err - end + handle_existing_entity(url, entity, options) e -> Logger.warn("Something failed while fetching url #{inspect(e)}") @@ -139,6 +106,54 @@ defmodule Mobilizon.Federation.ActivityPub do end end + @spec handle_existing_entity(String.t(), struct(), Keyword.t()) :: + {:ok, struct()} + | {:ok, struct()} + | {:error, String.t(), struct()} + | {:error, String.t()} + defp handle_existing_entity(url, entity, options) do + Logger.debug("Entity is already existing") + Logger.debug("Going to preload an existing entity") + + case refresh_entity(url, entity, options) do + {:ok, entity} -> + Preloader.maybe_preload(entity) + + {:error, status, entity} -> + {:ok, entity} = Preloader.maybe_preload(entity) + {:error, status, entity} + + err -> + err + end + end + + @spec refresh_entity(String.t(), struct(), Keyword.t()) :: + {:ok, struct()} | {:error, String.t(), struct()} | {:error, String.t()} + defp refresh_entity(url, entity, options) do + force_fetch = Keyword.get(options, :force, false) + + if force_fetch and not are_same_origin?(url, Endpoint.url()) do + Logger.debug("Entity is external and we want a force fetch") + + case Fetcher.fetch_and_update(url, options) do + {:ok, _activity, entity} -> + {:ok, entity} + + {:error, "Gone"} -> + {:error, "Gone", entity} + + {:error, "Not found"} -> + {:error, "Not found", entity} + + {:error, "Object origin check failed"} -> + {:error, "Object origin check failed"} + end + else + {:ok, entity} + end + end + @doc """ Getting an actor from url, eventually creating it if we don't have it locally or if it needs an update """ @@ -165,8 +180,8 @@ defmodule Mobilizon.Federation.ActivityPub do {:ok, %Actor{} = actor} -> {:ok, actor} - err -> - Logger.warn("Could not fetch by AP id") + {:error, err} -> + Logger.debug("Could not fetch by AP id") Logger.debug(inspect(err)) {:error, "Could not fetch by AP id"} end @@ -624,10 +639,6 @@ defmodule Mobilizon.Federation.ActivityPub do {:error, e} -> Logger.warn("Failed to make actor from url") {:error, e} - - e -> - Logger.warn("Failed to make actor from url") - {:error, e} end end end @@ -784,7 +795,7 @@ defmodule Mobilizon.Federation.ActivityPub do end # Fetching a remote actor's information through its AP ID - @spec fetch_and_prepare_actor_from_url(String.t()) :: {:ok, struct()} | {:error, atom()} | any() + @spec fetch_and_prepare_actor_from_url(String.t()) :: {:ok, map()} | {:error, atom()} | any() defp fetch_and_prepare_actor_from_url(url) do Logger.debug("Fetching and preparing actor from url") Logger.debug(inspect(url)) diff --git a/lib/federation/activity_pub/federator.ex b/lib/federation/activity_pub/federator.ex index d9027084f..8d0e7a7f9 100644 --- a/lib/federation/activity_pub/federator.ex +++ b/lib/federation/activity_pub/federator.ex @@ -61,7 +61,7 @@ defmodule Mobilizon.Federation.ActivityPub.Federator do e -> # Just drop those for now - Logger.error("Unhandled activity") + Logger.debug("Unhandled activity") Logger.debug(inspect(e)) Logger.debug(Jason.encode!(params)) end diff --git a/lib/federation/activity_pub/fetcher.ex b/lib/federation/activity_pub/fetcher.ex index 65971b6c7..7808c9710 100644 --- a/lib/federation/activity_pub/fetcher.ex +++ b/lib/federation/activity_pub/fetcher.ex @@ -30,11 +30,11 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do {:ok, data} else {:ok, %Tesla.Env{status: 410}} -> - Logger.warn("Resource at #{url} is 410 Gone") + Logger.debug("Resource at #{url} is 410 Gone") {:error, "Gone"} {:ok, %Tesla.Env{status: 404}} -> - Logger.warn("Resource at #{url} is 404 Gone") + Logger.debug("Resource at #{url} is 404 Gone") {:error, "Not found"} {:ok, %Tesla.Env{} = res} -> @@ -75,7 +75,7 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do @spec fetch_and_update(String.t(), Keyword.t()) :: {:ok, map(), struct()} def fetch_and_update(url, options \\ []) do with {:ok, data} when is_map(data) <- fetch(url, options), - {:origin_check, true} <- {:origin_check, origin_check?(url, data)}, + {:origin_check, true} <- {:origin_check, origin_check(url, data)}, params <- %{ "type" => "Update", "to" => data["to"], @@ -87,7 +87,6 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do Transmogrifier.handle_incoming(params) else {:origin_check, false} -> - Logger.warn("Object origin check failed") {:error, "Object origin check failed"} {:error, err} -> @@ -95,6 +94,17 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do end end + @spec origin_check(String.t(), map()) :: boolean() + defp origin_check(url, data) do + if origin_check?(url, data) do + true + else + Sentry.capture_message("Object origin check failed", extra: %{url: url, data: data}) + Logger.debug("Object origin check failed") + false + end + end + @spec address_invalid(String.t()) :: false | {:error, :invalid_url} defp address_invalid(address) do with %URI{host: host, scheme: scheme} <- URI.parse(address), diff --git a/lib/federation/activity_pub/preloader.ex b/lib/federation/activity_pub/preloader.ex index 79db7a65a..bf8325acc 100644 --- a/lib/federation/activity_pub/preloader.ex +++ b/lib/federation/activity_pub/preloader.ex @@ -12,6 +12,7 @@ defmodule Mobilizon.Federation.ActivityPub.Preloader do alias Mobilizon.Resources.Resource alias Mobilizon.Tombstone + @spec maybe_preload(struct()) :: {:ok, struct()} | {:error, struct()} def maybe_preload(%Event{url: url}), do: {:ok, Events.get_public_event_by_url_with_preload!(url)} diff --git a/lib/federation/activity_pub/refresher.ex b/lib/federation/activity_pub/refresher.ex index 6f485ba4b..e4ff02be5 100644 --- a/lib/federation/activity_pub/refresher.ex +++ b/lib/federation/activity_pub/refresher.ex @@ -7,7 +7,6 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.{Fetcher, Relay, Transmogrifier, Utils} - alias Mobilizon.Storage.Repo require Logger @doc """ @@ -60,9 +59,23 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do :ok <- fetch_collection(events_url, on_behalf_of) do :ok else + {:error, err} -> + Logger.error("Error while refreshing a group") + + Sentry.capture_message("Error while refreshing a group", + extra: %{group_url: group_url} + ) + + Logger.debug(inspect(err)) + err -> Logger.error("Error while refreshing a group") - Logger.error(inspect(err)) + + Sentry.capture_message("Error while refreshing a group", + extra: %{group_url: group_url} + ) + + Logger.debug(inspect(err)) end end @@ -96,14 +109,11 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do end end - @spec refresh_all_external_groups :: any() + @spec refresh_all_external_groups :: :ok def refresh_all_external_groups do - Repo.transaction(fn -> - Actors.list_external_groups_for_stream() - |> Stream.filter(&Actors.needs_update?/1) - |> Stream.map(&refresh_profile/1) - |> Stream.run() - end) + Actors.list_external_groups() + |> Enum.filter(&Actors.needs_update?/1) + |> Enum.each(&refresh_profile/1) end defp process_collection(%{"type" => type, "orderedItems" => items}, _on_behalf_of) @@ -122,6 +132,14 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do :ok end + # Lemmy uses an OrderedCollection with the items property + defp process_collection(%{"type" => type, "items" => items} = collection, on_behalf_of) + when type in ["OrderedCollection", "OrderedCollectionPage"] do + collection + |> Map.put("orderedItems", items) + |> process_collection(on_behalf_of) + end + defp process_collection(%{"type" => "OrderedCollection", "first" => first}, on_behalf_of) when is_map(first), do: process_collection(first, on_behalf_of) @@ -150,6 +168,11 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do Transmogrifier.handle_incoming(data) end + # If we're handling an announce activity + defp handling_element(%{"type" => "Announce"} = data) do + handling_element(get_in(data, ["object"])) + end + # If we're handling directly an object defp handling_element(data) when is_map(data) do object = get_in(data, ["object"]) diff --git a/lib/federation/activity_pub/transmogrifier.ex b/lib/federation/activity_pub/transmogrifier.ex index 620c2e747..1e5ab2fb1 100644 --- a/lib/federation/activity_pub/transmogrifier.ex +++ b/lib/federation/activity_pub/transmogrifier.ex @@ -371,11 +371,13 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do end end - def handle_incoming(%{ - "type" => "Update", - "object" => %{"type" => object_type} = object, - "actor" => _actor_id - }) + def handle_incoming( + %{ + "type" => "Update", + "object" => %{"type" => object_type} = object, + "actor" => _actor_id + } = params + ) when object_type in ["Person", "Group", "Application", "Service", "Organization"] do with {:ok, %Actor{suspended: false} = old_actor} <- ActivityPub.get_or_fetch_actor_by_url(object["id"]), @@ -386,7 +388,11 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do {:ok, activity, new_actor} else e -> - Logger.error(inspect(e)) + Sentry.capture_message("Error while handling an Update activity", + extra: %{params: params} + ) + + Logger.debug(inspect(e)) :error end end @@ -572,7 +578,8 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do %{"type" => "Delete", "object" => object, "actor" => _actor, "id" => _id} = data ) do with actor_url <- Utils.get_actor(data), - {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(actor_url), + {:actor, {:ok, %Actor{} = actor}} <- + {:actor, ActivityPub.get_or_fetch_actor_by_url(actor_url)}, object_id <- Utils.get_url(object), {:ok, object} <- is_group_object_gone(object_id), {:origin_check, true} <- @@ -586,8 +593,25 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do Logger.warn("Object origin check failed") :error + {:actor, {:error, "Could not fetch by AP id"}} -> + {:error, :unknown_actor} + + {:error, e} -> + Logger.debug(inspect(e)) + + # Sentry.capture_message("Error while handling a Delete activity", + # extra: %{data: data} + # ) + + :error + e -> Logger.error(inspect(e)) + + # Sentry.capture_message("Error while handling a Delete activity", + # extra: %{data: data} + # ) + :error end end @@ -610,7 +634,12 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do {:ok, activity, new_resource} else e -> - Logger.error(inspect(e)) + Logger.debug(inspect(e)) + + Sentry.capture_message("Error while handling an Move activity", + extra: %{data: data} + ) + :error end end @@ -741,6 +770,11 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do def handle_incoming(object) do Logger.info("Handing something with type #{object["type"]} not supported") Logger.debug(inspect(object)) + + Sentry.capture_message("Handing something with type #{object["type"]} not supported", + extra: %{object: object} + ) + {:error, :not_supported} end diff --git a/lib/federation/activity_pub/utils.ex b/lib/federation/activity_pub/utils.ex index b21a5bdcc..d1cb0a3ed 100644 --- a/lib/federation/activity_pub/utils.ex +++ b/lib/federation/activity_pub/utils.ex @@ -259,7 +259,8 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do are_same_origin?(id, actor) end - def origin_check?(_id, %{"type" => type} = _params) when type in ["Actor", "Group"], do: true + def origin_check?(_id, %{"type" => type} = _params) when type in ["Actor", "Person", "Group"], + do: true def origin_check?(_id, %{"actor" => nil} = _args), do: false @@ -701,4 +702,42 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do true end end + + @spec label_in_collection?(any(), any()) :: boolean() + defp label_in_collection?(url, coll) when is_binary(coll), do: url == coll + defp label_in_collection?(url, coll) when is_list(coll), do: url in coll + defp label_in_collection?(_, _), do: false + + @spec label_in_message?(String.t(), map()) :: boolean() + def label_in_message?(label, params), + do: + [params["to"], params["cc"], params["bto"], params["bcc"]] + |> Enum.any?(&label_in_collection?(label, &1)) + + @spec unaddressed_message?(map()) :: boolean() + def unaddressed_message?(params), + do: + [params["to"], params["cc"], params["bto"], params["bcc"]] + |> Enum.all?(&is_nil(&1)) + + @spec recipient_in_message(Actor.t(), Actor.t(), map()) :: boolean() + def recipient_in_message(%Actor{url: url} = _recipient, %Actor{} = _actor, params), + do: label_in_message?(url, params) || unaddressed_message?(params) + + defp extract_list(target) when is_binary(target), do: [target] + defp extract_list(lst) when is_list(lst), do: lst + defp extract_list(_), do: [] + + def maybe_splice_recipient(url, params) do + need_splice? = + !label_in_collection?(url, params["to"]) && + !label_in_collection?(url, params["cc"]) + + if need_splice? do + cc_list = extract_list(params["cc"]) + Map.put(params, "cc", [url | cc_list]) + else + params + end + end end diff --git a/lib/federation/http_signatures/signature.ex b/lib/federation/http_signatures/signature.ex index bab2b14d7..787b4ccb4 100644 --- a/lib/federation/http_signatures/signature.ex +++ b/lib/federation/http_signatures/signature.ex @@ -50,7 +50,8 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do # Gets a public key for a given ActivityPub actor ID (url). @spec get_public_key_for_url(String.t()) :: - {:ok, String.t()} | {:error, :actor_fetch_error | :pem_decode_error} + {:ok, String.t()} + | {:error, :actor_fetch_error | :pem_decode_error | :actor_not_fetchable} defp get_public_key_for_url(url) do with {:ok, %Actor{keys: keys}} <- ActivityPub.get_or_fetch_actor_by_url(url), {:ok, public_key} <- prepare_public_key(keys) do @@ -61,8 +62,16 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do {:error, :pem_decode_error} - _ -> + {:error, "Could not fetch by AP id"} -> + {:error, :actor_not_fetchable} + + err -> + Sentry.capture_message("Unable to fetch actor, so no keys for you", + extra: %{url: url} + ) + Logger.error("Unable to fetch actor, so no keys for you") + Logger.error(inspect(err)) {:error, :actor_fetch_error} end @@ -74,9 +83,6 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do :ok <- Logger.debug("Fetching public key for #{actor_id}"), {:ok, public_key} <- get_public_key_for_url(actor_id) do {:ok, public_key} - else - e -> - {:error, e} end end @@ -87,9 +93,6 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do {:ok, _actor} <- ActivityPub.make_actor_from_url(actor_id), {:ok, public_key} <- get_public_key_for_url(actor_id) do {:ok, public_key} - else - e -> - {:error, e} end end diff --git a/lib/mobilizon/actors/actors.ex b/lib/mobilizon/actors/actors.ex index aabdf21ef..4ae932e27 100644 --- a/lib/mobilizon/actors/actors.ex +++ b/lib/mobilizon/actors/actors.ex @@ -374,12 +374,22 @@ defmodule Mobilizon.Actors do {:error, remove, error, _} when remove in [:remove_banner, :remove_avatar] -> Logger.error("Error while deleting actor's banner or avatar") - Logger.error(inspect(error, pretty: true)) + + Sentry.capture_message("Error while deleting actor's banner or avatar", + extra: %{err: error} + ) + + Logger.debug(inspect(error, pretty: true)) {:error, error} err -> Logger.error("Unknown error while deleting actor") - Logger.error(inspect(err, pretty: true)) + + Sentry.capture_message("Error while deleting actor's banner or avatar", + extra: %{err: err} + ) + + Logger.debug(inspect(err, pretty: true)) {:error, err} end end @@ -652,10 +662,11 @@ defmodule Mobilizon.Actors do @doc """ Lists the groups. """ - @spec list_groups_for_stream :: Enum.t() - def list_external_groups_for_stream do + @spec list_external_groups(non_neg_integer()) :: list(Actor.t()) + def list_external_groups(limit \\ 100) when limit > 0 do external_groups_query() - |> Repo.stream() + |> limit(^limit) + |> Repo.all() end @doc """ diff --git a/lib/web/controllers/activity_pub_controller.ex b/lib/web/controllers/activity_pub_controller.ex index 0e35181c4..909369976 100644 --- a/lib/web/controllers/activity_pub_controller.ex +++ b/lib/web/controllers/activity_pub_controller.ex @@ -10,7 +10,7 @@ defmodule Mobilizon.Web.ActivityPubController do alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Federation.ActivityPub - alias Mobilizon.Federation.ActivityPub.Federator + alias Mobilizon.Federation.ActivityPub.{Federator, Utils} alias Mobilizon.Web.ActivityPub.ActorView alias Mobilizon.Web.Cache @@ -105,7 +105,17 @@ defmodule Mobilizon.Web.ActivityPubController do actor_collection(conn, "outbox", args) end - # TODO: Ensure that this inbox is a recipient of the message + def inbox(%{assigns: %{valid_signature: true}} = conn, %{"name" => preferred_username} = params) do + with %Actor{url: recipient_url} = recipient <- + Actors.get_local_actor_by_name(preferred_username), + {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(params["actor"]), + true <- Utils.recipient_in_message(recipient, actor, params), + params <- Utils.maybe_splice_recipient(recipient_url, params) do + Federator.enqueue(:incoming_ap_doc, params) + json(conn, "ok") + end + end + def inbox(%{assigns: %{valid_signature: true}} = conn, params) do Logger.debug("Got something with valid signature inside inbox") Federator.enqueue(:incoming_ap_doc, params) @@ -114,7 +124,7 @@ defmodule Mobilizon.Web.ActivityPubController do # only accept relayed Creates def inbox(conn, %{"type" => "Create"} = params) do - Logger.info( + Logger.debug( "Signature missing or not from author, relayed Create message, fetching object from source" ) @@ -126,8 +136,9 @@ defmodule Mobilizon.Web.ActivityPubController do def inbox(conn, params) do headers = Enum.into(conn.req_headers, %{}) - if String.contains?(headers["signature"], params["actor"]) do - Logger.error( + if headers["signature"] && params["actor"] && + String.contains?(headers["signature"], params["actor"]) do + Logger.debug( "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!" ) diff --git a/lib/web/views/error_view.ex b/lib/web/views/error_view.ex index 7eac0ca4d..4cbc238a2 100644 --- a/lib/web/views/error_view.ex +++ b/lib/web/views/error_view.ex @@ -47,6 +47,10 @@ defmodule Mobilizon.Web.ErrorView do %{msg: "Not acceptable"} end + def render("500.json", assigns) do + render("500.html", assigns) + end + def render("500.html", assigns) do Mobilizon.Config.instance_config() |> Keyword.get(:default_language, "en") From 17a6a6eadac3cb0a2ce7077a321204cc7b4d3c67 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 22 Apr 2021 17:48:43 +0200 Subject: [PATCH 02/31] Add an unique index on addresses url Signed-off-by: Thomas Citharel --- lib/mobilizon/addresses/address.ex | 1 + .../20210422140923_cleanup_addresses.exs | 75 +++++++++++++++++++ .../20210422155913_add_index_to_addresses.exs | 11 +++ 3 files changed, 87 insertions(+) create mode 100644 priv/repo/migrations/20210422140923_cleanup_addresses.exs create mode 100644 priv/repo/migrations/20210422155913_add_index_to_addresses.exs diff --git a/lib/mobilizon/addresses/address.ex b/lib/mobilizon/addresses/address.ex index c2d71bb20..7485ee7a9 100644 --- a/lib/mobilizon/addresses/address.ex +++ b/lib/mobilizon/addresses/address.ex @@ -63,6 +63,7 @@ defmodule Mobilizon.Addresses.Address do |> cast(attrs, @attrs) |> set_url() |> validate_required(@required_attrs) + |> unique_constraint(:url, name: :addresses_url_index) end @spec set_url(Ecto.Changeset.t()) :: Ecto.Changeset.t() diff --git a/priv/repo/migrations/20210422140923_cleanup_addresses.exs b/priv/repo/migrations/20210422140923_cleanup_addresses.exs new file mode 100644 index 000000000..695122595 --- /dev/null +++ b/priv/repo/migrations/20210422140923_cleanup_addresses.exs @@ -0,0 +1,75 @@ +defmodule Mobilizon.Storage.Repo.Migrations.CleanupAddresses do + use Ecto.Migration + + def up do + # Make sure we don't have any duplicate addresses + rows = fetch_bad_rows() + Enum.each(rows, &process_row/1) + + flush() + end + + def down do + # No way down + end + + defp fetch_bad_rows() do + %Postgrex.Result{rows: rows} = + Ecto.Adapters.SQL.query!( + Mobilizon.Storage.Repo, + "SELECT * FROM ( + SELECT id, url, + ROW_NUMBER() OVER(PARTITION BY url ORDER BY id asc) AS Row + FROM addresses + ) dups + WHERE dups.Row > 1;" + ) + + rows + end + + defp process_row([id, url, _row]) do + first_id = find_first_address_id(url) + + if id != first_id do + repair_events(id, first_id) + repair_actors(id, first_id) + delete_row(id) + end + end + + defp find_first_address_id(url) do + %Postgrex.Result{rows: [[id]]} = + Ecto.Adapters.SQL.query!( + Mobilizon.Storage.Repo, + "SELECT id FROM addresses WHERE url = $1 order by id asc limit 1", + [url] + ) + + id + end + + defp repair_events(id, first_id) do + Ecto.Adapters.SQL.query!( + Mobilizon.Storage.Repo, + "UPDATE events SET physical_address_id = $1 WHERE physical_address_id = $2", + [first_id, id] + ) + end + + defp repair_actors(id, first_id) do + Ecto.Adapters.SQL.query!( + Mobilizon.Storage.Repo, + "UPDATE actors SET physical_address_id = $1 WHERE physical_address_id = $2", + [first_id, id] + ) + end + + defp delete_row(id) do + Ecto.Adapters.SQL.query!( + Mobilizon.Storage.Repo, + "DELETE FROM addresses WHERE id = $1", + [id] + ) + end +end diff --git a/priv/repo/migrations/20210422155913_add_index_to_addresses.exs b/priv/repo/migrations/20210422155913_add_index_to_addresses.exs new file mode 100644 index 000000000..1a08a28fd --- /dev/null +++ b/priv/repo/migrations/20210422155913_add_index_to_addresses.exs @@ -0,0 +1,11 @@ +defmodule Mobilizon.Storage.Repo.Migrations.AddIndexToAddresses do + use Ecto.Migration + + def up do + create_if_not_exists(unique_index("addresses", [:url])) + end + + def down do + drop_if_exists(index("addresses", [:url])) + end +end From 280f461ba7681c83b4eb80a3613ad609f3beb435 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 22 Apr 2021 12:17:56 +0200 Subject: [PATCH 03/31] Refactor the ActivityPub module Signed-off-by: Thomas Citharel --- lib/federation/activity_pub/activity_pub.ex | 134 +----------------- lib/federation/activity_pub/actor.ex | 102 +++++++++++++ lib/federation/activity_pub/federator.ex | 4 +- lib/federation/activity_pub/fetcher.ex | 37 +++++ lib/federation/activity_pub/refresher.ex | 5 +- lib/federation/activity_pub/relay.ex | 16 ++- lib/federation/activity_pub/transmogrifier.ex | 59 ++++---- lib/federation/activity_pub/utils.ex | 8 +- .../activity_stream/converter/discussion.ex | 6 +- .../activity_stream/converter/flag.ex | 7 +- .../activity_stream/converter/member.ex | 6 +- .../activity_stream/converter/post.ex | 6 +- .../activity_stream/converter/resource.ex | 5 +- .../activity_stream/converter/todo.ex | 3 +- .../activity_stream/converter/todo_list.ex | 4 +- .../activity_stream/converter/utils.ex | 7 +- lib/federation/http_signatures/signature.ex | 6 +- lib/federation/web_finger/web_finger.ex | 4 +- lib/graphql/api/search.ex | 3 +- lib/graphql/resolvers/group.ex | 5 +- lib/graphql/resolvers/member.ex | 3 +- lib/graphql/resolvers/person.ex | 3 +- lib/mix/tasks/mobilizon/actors/refresh.ex | 6 +- .../controllers/activity_pub_controller.ex | 3 +- lib/web/plugs/mapped_signature_to_identity.ex | 4 +- .../activity_pub/activity_pub_test.exs | 115 +-------------- test/federation/activity_pub/actor_test.exs | 120 ++++++++++++++++ .../transmogrifier/delete_test.exs | 10 +- .../transmogrifier/posts_test.exs | 1 - .../activity_pub/transmogrifier_test.exs | 11 +- test/graphql/api/search_test.exs | 5 +- test/mobilizon/actors/actors_test.exs | 9 +- 32 files changed, 385 insertions(+), 332 deletions(-) create mode 100644 lib/federation/activity_pub/actor.ex create mode 100644 test/federation/activity_pub/actor_test.exs diff --git a/lib/federation/activity_pub/activity_pub.ex b/lib/federation/activity_pub/activity_pub.ex index 08cd8e825..67be09626 100644 --- a/lib/federation/activity_pub/activity_pub.ex +++ b/lib/federation/activity_pub/activity_pub.ex @@ -39,11 +39,12 @@ defmodule Mobilizon.Federation.ActivityPub do Visibility } + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor + alias Mobilizon.Federation.ActivityPub.Types.{Managable, Ownable} - alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} + alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.HTTPSignatures.Signature - alias Mobilizon.Federation.WebFinger alias Mobilizon.Service.Notifications.Scheduler alias Mobilizon.Storage.Page @@ -154,40 +155,6 @@ defmodule Mobilizon.Federation.ActivityPub do end end - @doc """ - Getting an actor from url, eventually creating it if we don't have it locally or if it needs an update - """ - @spec get_or_fetch_actor_by_url(String.t(), boolean) :: {:ok, Actor.t()} | {:error, String.t()} - def get_or_fetch_actor_by_url(url, preload \\ false) - - def get_or_fetch_actor_by_url(nil, _preload), do: {:error, "Can't fetch a nil url"} - - def get_or_fetch_actor_by_url("https://www.w3.org/ns/activitystreams#Public", _preload) do - with %Actor{url: url} <- Relay.get_actor() do - get_or_fetch_actor_by_url(url) - end - end - - @spec get_or_fetch_actor_by_url(String.t(), boolean()) :: {:ok, Actor.t()} | {:error, any()} - def get_or_fetch_actor_by_url(url, preload) do - with {:ok, %Actor{} = cached_actor} <- Actors.get_actor_by_url(url, preload), - false <- Actors.needs_update?(cached_actor) do - {:ok, cached_actor} - else - _ -> - # For tests, see https://github.com/jjh42/mock#not-supported---mocking-internal-function-calls and Mobilizon.Federation.ActivityPubTest - case __MODULE__.make_actor_from_url(url, preload) do - {:ok, %Actor{} = actor} -> - {:ok, actor} - - {:error, err} -> - Logger.debug("Could not fetch by AP id") - Logger.debug(inspect(err)) - {:error, "Could not fetch by AP id"} - end - end - end - @doc """ Create an activity of type `Create` @@ -302,7 +269,8 @@ defmodule Mobilizon.Federation.ActivityPub do local \\ true, public \\ true ) do - with {:ok, %Actor{id: object_owner_actor_id}} <- get_or_fetch_actor_by_url(object["actor"]), + with {:ok, %Actor{id: object_owner_actor_id}} <- + ActivityPubActor.get_or_fetch_actor_by_url(object["actor"]), {:ok, %Share{} = _share} <- Share.create(object["id"], actor.id, object_owner_actor_id), announce_data <- make_announce_data(actor, object, activity_id, public), {:ok, activity} <- create_activity(announce_data, local), @@ -619,64 +587,6 @@ defmodule Mobilizon.Federation.ActivityPub do end end - @doc """ - Create an actor locally by its URL (AP ID) - """ - @spec make_actor_from_url(String.t(), boolean()) :: {:ok, %Actor{}} | {:error, any()} - def make_actor_from_url(url, preload \\ false) do - if are_same_origin?(url, Endpoint.url()) do - {:error, "Can't make a local actor from URL"} - else - case fetch_and_prepare_actor_from_url(url) do - {:ok, data} -> - Actors.upsert_actor(data, preload) - - # Request returned 410 - {:error, :actor_deleted} -> - Logger.info("Actor was deleted") - {:error, :actor_deleted} - - {:error, e} -> - Logger.warn("Failed to make actor from url") - {:error, e} - end - end - end - - @doc """ - Find an actor in our local database or call WebFinger to find what's its AP ID is and then fetch it - """ - @spec find_or_make_actor_from_nickname(String.t(), atom() | nil) :: tuple() - def find_or_make_actor_from_nickname(nickname, type \\ nil) do - case Actors.get_actor_by_name(nickname, type) do - %Actor{} = actor -> - {:ok, actor} - - nil -> - make_actor_from_nickname(nickname) - end - end - - @spec find_or_make_person_from_nickname(String.t()) :: tuple() - def find_or_make_person_from_nickname(nick), do: find_or_make_actor_from_nickname(nick, :Person) - - @spec find_or_make_group_from_nickname(String.t()) :: tuple() - def find_or_make_group_from_nickname(nick), do: find_or_make_actor_from_nickname(nick, :Group) - - @doc """ - Create an actor inside our database from username, using WebFinger to find out its AP ID and then fetch it - """ - @spec make_actor_from_nickname(String.t()) :: {:ok, %Actor{}} | {:error, any()} - def make_actor_from_nickname(nickname) do - case WebFinger.finger(nickname) do - {:ok, url} when is_binary(url) -> - make_actor_from_url(url) - - _e -> - {:error, "No ActivityPub URL found in WebFinger"} - end - end - @spec is_create_activity?(Activity.t()) :: boolean defp is_create_activity?(%Activity{data: %{"type" => "Create"}}), do: true defp is_create_activity?(_), do: false @@ -794,40 +704,6 @@ defmodule Mobilizon.Federation.ActivityPub do ) end - # Fetching a remote actor's information through its AP ID - @spec fetch_and_prepare_actor_from_url(String.t()) :: {:ok, map()} | {:error, atom()} | any() - defp fetch_and_prepare_actor_from_url(url) do - Logger.debug("Fetching and preparing actor from url") - Logger.debug(inspect(url)) - - res = - with {:ok, %{status: 200, body: body}} <- - Tesla.get(url, - headers: [{"Accept", "application/activity+json"}], - follow_redirect: true - ), - :ok <- Logger.debug("response okay, now decoding json"), - {:ok, data} <- Jason.decode(body) do - Logger.debug("Got activity+json response at actor's endpoint, now converting data") - {:ok, Converter.Actor.as_to_model_data(data)} - else - # Actor is gone, probably deleted - {:ok, %{status: 410}} -> - Logger.info("Response HTTP 410") - {:error, :actor_deleted} - - {:error, e} -> - Logger.warn("Could not decode actor at fetch #{url}, #{inspect(e)}") - {:error, e} - - e -> - Logger.warn("Could not decode actor at fetch #{url}, #{inspect(e)}") - {:error, e} - end - - res - end - @doc """ Return all public activities (events & comments) for an actor """ diff --git a/lib/federation/activity_pub/actor.ex b/lib/federation/activity_pub/actor.ex new file mode 100644 index 000000000..aad2f2f6a --- /dev/null +++ b/lib/federation/activity_pub/actor.ex @@ -0,0 +1,102 @@ +defmodule Mobilizon.Federation.ActivityPub.Actor do + @moduledoc """ + Module to handle ActivityPub Actor interactions + """ + + alias Mobilizon.Actors + alias Mobilizon.Actors.Actor + alias Mobilizon.Federation.ActivityPub.{Fetcher, Relay} + alias Mobilizon.Federation.WebFinger + alias Mobilizon.Web.Endpoint + require Logger + import Mobilizon.Federation.ActivityPub.Utils, only: [are_same_origin?: 2] + + @doc """ + Getting an actor from url, eventually creating it if we don't have it locally or if it needs an update + """ + @spec get_or_fetch_actor_by_url(String.t(), boolean) :: {:ok, Actor.t()} | {:error, String.t()} + def get_or_fetch_actor_by_url(url, preload \\ false) + + def get_or_fetch_actor_by_url(nil, _preload), do: {:error, "Can't fetch a nil url"} + + def get_or_fetch_actor_by_url("https://www.w3.org/ns/activitystreams#Public", _preload) do + with %Actor{url: url} <- Relay.get_actor() do + get_or_fetch_actor_by_url(url) + end + end + + @spec get_or_fetch_actor_by_url(String.t(), boolean()) :: {:ok, Actor.t()} | {:error, any()} + def get_or_fetch_actor_by_url(url, preload) do + with {:ok, %Actor{} = cached_actor} <- Actors.get_actor_by_url(url, preload), + false <- Actors.needs_update?(cached_actor) do + {:ok, cached_actor} + else + _ -> + # For tests, see https://github.com/jjh42/mock#not-supported---mocking-internal-function-calls and Mobilizon.Federation.ActivityPubTest + case __MODULE__.make_actor_from_url(url, preload) do + {:ok, %Actor{} = actor} -> + {:ok, actor} + + {:error, err} -> + Logger.debug("Could not fetch by AP id") + Logger.debug(inspect(err)) + {:error, "Could not fetch by AP id"} + end + end + end + + @doc """ + Create an actor locally by its URL (AP ID) + """ + @spec make_actor_from_url(String.t(), boolean()) :: {:ok, %Actor{}} | {:error, any()} + def make_actor_from_url(url, preload \\ false) do + if are_same_origin?(url, Endpoint.url()) do + {:error, "Can't make a local actor from URL"} + else + case Fetcher.fetch_and_prepare_actor_from_url(url) do + {:ok, data} -> + Actors.upsert_actor(data, preload) + + # Request returned 410 + {:error, :actor_deleted} -> + Logger.info("Actor was deleted") + {:error, :actor_deleted} + + {:error, e} -> + Logger.warn("Failed to make actor from url") + {:error, e} + end + end + end + + @doc """ + Find an actor in our local database or call WebFinger to find what's its AP ID is and then fetch it + """ + @spec find_or_make_actor_from_nickname(String.t(), atom() | nil) :: tuple() + def find_or_make_actor_from_nickname(nickname, type \\ nil) do + case Actors.get_actor_by_name(nickname, type) do + %Actor{} = actor -> + {:ok, actor} + + nil -> + make_actor_from_nickname(nickname) + end + end + + @spec find_or_make_group_from_nickname(String.t()) :: tuple() + def find_or_make_group_from_nickname(nick), do: find_or_make_actor_from_nickname(nick, :Group) + + @doc """ + Create an actor inside our database from username, using WebFinger to find out its AP ID and then fetch it + """ + @spec make_actor_from_nickname(String.t()) :: {:ok, %Actor{}} | {:error, any()} + def make_actor_from_nickname(nickname) do + case WebFinger.finger(nickname) do + {:ok, url} when is_binary(url) -> + make_actor_from_url(url) + + _e -> + {:error, "No ActivityPub URL found in WebFinger"} + end + end +end diff --git a/lib/federation/activity_pub/federator.ex b/lib/federation/activity_pub/federator.ex index 8d0e7a7f9..4bed74643 100644 --- a/lib/federation/activity_pub/federator.ex +++ b/lib/federation/activity_pub/federator.ex @@ -13,6 +13,7 @@ defmodule Mobilizon.Federation.ActivityPub.Federator do alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier} + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor require Logger @@ -42,7 +43,8 @@ defmodule Mobilizon.Federation.ActivityPub.Federator do Logger.debug(inspect(activity)) Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end) - with {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(activity.data["actor"]) do + with {:ok, %Actor{} = actor} <- + ActivityPubActor.get_or_fetch_actor_by_url(activity.data["actor"]) do Logger.info(fn -> "Sending #{activity.data["id"]} out via AP" end) ActivityPub.publish(actor, activity) end diff --git a/lib/federation/activity_pub/fetcher.ex b/lib/federation/activity_pub/fetcher.ex index 7808c9710..7d1b73d63 100644 --- a/lib/federation/activity_pub/fetcher.ex +++ b/lib/federation/activity_pub/fetcher.ex @@ -8,6 +8,7 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do alias Mobilizon.Federation.HTTPSignatures.Signature alias Mobilizon.Federation.ActivityPub.{Relay, Transmogrifier} + alias Mobilizon.Federation.ActivityStream.Converter.Actor, as: ActorConverter alias Mobilizon.Service.HTTP.ActivityPub, as: ActivityPubClient import Mobilizon.Federation.ActivityPub.Utils, @@ -94,6 +95,42 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do end end + @doc """ + Fetching a remote actor's information through its AP ID + """ + @spec fetch_and_prepare_actor_from_url(String.t()) :: {:ok, map()} | {:error, atom()} | any() + def fetch_and_prepare_actor_from_url(url) do + Logger.debug("Fetching and preparing actor from url") + Logger.debug(inspect(url)) + + res = + with {:ok, %{status: 200, body: body}} <- + Tesla.get(url, + headers: [{"Accept", "application/activity+json"}], + follow_redirect: true + ), + :ok <- Logger.debug("response okay, now decoding json"), + {:ok, data} <- Jason.decode(body) do + Logger.debug("Got activity+json response at actor's endpoint, now converting data") + {:ok, ActorConverter.as_to_model_data(data)} + else + # Actor is gone, probably deleted + {:ok, %{status: 410}} -> + Logger.info("Response HTTP 410") + {:error, :actor_deleted} + + {:error, e} -> + Logger.warn("Could not decode actor at fetch #{url}, #{inspect(e)}") + {:error, e} + + e -> + Logger.warn("Could not decode actor at fetch #{url}, #{inspect(e)}") + {:error, e} + end + + res + end + @spec origin_check(String.t(), map()) :: boolean() defp origin_check(url, data) do if origin_check?(url, data) do diff --git a/lib/federation/activity_pub/refresher.ex b/lib/federation/activity_pub/refresher.ex index e4ff02be5..dec998427 100644 --- a/lib/federation/activity_pub/refresher.ex +++ b/lib/federation/activity_pub/refresher.ex @@ -6,6 +6,7 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do alias Mobilizon.Actors alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.{Fetcher, Relay, Transmogrifier, Utils} require Logger @@ -31,7 +32,7 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do end def refresh_profile(%Actor{type: type, url: url}) when type in [:Person, :Application] do - with {:ok, %Actor{outbox_url: outbox_url}} <- ActivityPub.make_actor_from_url(url), + with {:ok, %Actor{outbox_url: outbox_url}} <- ActivityPubActor.make_actor_from_url(url), :ok <- fetch_collection(outbox_url, Relay.get_actor()) do :ok end @@ -49,7 +50,7 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do discussions_url: discussions_url, events_url: events_url }} <- - ActivityPub.make_actor_from_url(group_url), + ActivityPubActor.make_actor_from_url(group_url), :ok <- fetch_collection(outbox_url, on_behalf_of), :ok <- fetch_collection(members_url, on_behalf_of), :ok <- fetch_collection(resources_url, on_behalf_of), diff --git a/lib/federation/activity_pub/relay.ex b/lib/federation/activity_pub/relay.ex index b0a8997c9..8266d94f5 100644 --- a/lib/federation/activity_pub/relay.ex +++ b/lib/federation/activity_pub/relay.ex @@ -13,6 +13,7 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.{Activity, Refresher, Transmogrifier} + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.WebFinger alias Mobilizon.GraphQL.API.Follows @@ -37,7 +38,8 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do def follow(address) do with {:ok, target_instance} <- fetch_actor(address), %Actor{} = local_actor <- get_actor(), - {:ok, %Actor{} = target_actor} <- ActivityPub.get_or_fetch_actor_by_url(target_instance), + {:ok, %Actor{} = target_actor} <- + ActivityPubActor.get_or_fetch_actor_by_url(target_instance), {:ok, activity, follow} <- Follows.follow(local_actor, target_actor) do Logger.info("Relay: followed instance #{target_instance}; id=#{activity.data["id"]}") {:ok, activity, follow} @@ -56,7 +58,8 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do def unfollow(address) do with {:ok, target_instance} <- fetch_actor(address), %Actor{} = local_actor <- get_actor(), - {:ok, %Actor{} = target_actor} <- ActivityPub.get_or_fetch_actor_by_url(target_instance), + {:ok, %Actor{} = target_actor} <- + ActivityPubActor.get_or_fetch_actor_by_url(target_instance), {:ok, activity, follow} <- Follows.unfollow(local_actor, target_actor) do Logger.info("Relay: unfollowed instance #{target_instance}: id=#{activity.data["id"]}") {:ok, activity, follow} @@ -73,7 +76,8 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do with {:ok, target_instance} <- fetch_actor(address), %Actor{} = local_actor <- get_actor(), - {:ok, %Actor{} = target_actor} <- ActivityPub.get_or_fetch_actor_by_url(target_instance), + {:ok, %Actor{} = target_actor} <- + ActivityPubActor.get_or_fetch_actor_by_url(target_instance), {:ok, activity, follow} <- Follows.accept(target_actor, local_actor) do {:ok, activity, follow} end @@ -84,7 +88,8 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do with {:ok, target_instance} <- fetch_actor(address), %Actor{} = local_actor <- get_actor(), - {:ok, %Actor{} = target_actor} <- ActivityPub.get_or_fetch_actor_by_url(target_instance), + {:ok, %Actor{} = target_actor} <- + ActivityPubActor.get_or_fetch_actor_by_url(target_instance), {:ok, activity, follow} <- Follows.reject(target_actor, local_actor) do {:ok, activity, follow} end @@ -94,7 +99,8 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do Logger.debug("We're trying to refresh a remote instance") with {:ok, target_instance} <- fetch_actor(address), - {:ok, %Actor{} = target_actor} <- ActivityPub.get_or_fetch_actor_by_url(target_instance) do + {:ok, %Actor{} = target_actor} <- + ActivityPubActor.get_or_fetch_actor_by_url(target_instance) do Refresher.refresh_profile(target_actor) end end diff --git a/lib/federation/activity_pub/transmogrifier.ex b/lib/federation/activity_pub/transmogrifier.ex index 1e5ab2fb1..3399e9b9b 100644 --- a/lib/federation/activity_pub/transmogrifier.ex +++ b/lib/federation/activity_pub/transmogrifier.ex @@ -18,6 +18,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.{Activity, Refresher, Relay, Utils} + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Types.Ownable alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Tombstone @@ -117,12 +118,13 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do def handle_incoming(%{ "type" => "Create", - "object" => %{"type" => "Group", "id" => group_url} = _object - }) do - Logger.info("Handle incoming to create a group") + "object" => %{"type" => type, "id" => actor_url} = _object + }) + when type in ["Group", "Person", "Actor"] do + Logger.info("Handle incoming to create an actor") - with {:ok, %Actor{} = group} <- ActivityPub.get_or_fetch_actor_by_url(group_url) do - {:ok, nil, group} + with {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do + {:ok, nil, actor} end end @@ -201,8 +203,8 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do def handle_incoming( %{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = _data ) do - with {:ok, %Actor{} = followed} <- ActivityPub.get_or_fetch_actor_by_url(followed, true), - {:ok, %Actor{} = follower} <- ActivityPub.get_or_fetch_actor_by_url(follower), + with {:ok, %Actor{} = followed} <- ActivityPubActor.get_or_fetch_actor_by_url(followed, true), + {:ok, %Actor{} = follower} <- ActivityPubActor.get_or_fetch_actor_by_url(follower), {:ok, activity, object} <- ActivityPub.follow(follower, followed, id, false) do {:ok, activity, object} else @@ -221,7 +223,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do with {:existing_todo_list, nil} <- {:existing_todo_list, Todos.get_todo_list_by_url(object_url)}, - {:ok, %Actor{url: actor_url}} <- ActivityPub.get_or_fetch_actor_by_url(actor_url), + {:ok, %Actor{url: actor_url}} <- ActivityPubActor.get_or_fetch_actor_by_url(actor_url), object_data when is_map(object_data) <- object |> Converter.TodoList.as_to_model_data(), {:ok, %Activity{} = activity, %TodoList{} = todo_list} <- @@ -295,7 +297,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do } = data ) do with actor_url <- Utils.get_actor(data), - {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(actor_url), + {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(actor_url), {:object_not_found, {:ok, %Activity{} = activity, object}} <- {:object_not_found, do_handle_incoming_accept_following(accepted_object, actor) || @@ -328,7 +330,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do %{"type" => "Reject", "object" => rejected_object, "actor" => _actor, "id" => id} = data ) do with actor_url <- Utils.get_actor(data), - {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(actor_url), + {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(actor_url), {:object_not_found, {:ok, activity, object}} <- {:object_not_found, do_handle_incoming_reject_following(rejected_object, actor) || @@ -359,7 +361,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do ) do with actor_url <- Utils.get_actor(data), {:ok, %Actor{id: actor_id, suspended: false} = actor} <- - ActivityPub.get_or_fetch_actor_by_url(actor_url), + ActivityPubActor.get_or_fetch_actor_by_url(actor_url), :ok <- Logger.debug("Fetching contained object"), {:ok, entity} <- process_announce_data(object, actor), :ok <- eventually_create_share(object, entity, actor_id) do @@ -380,7 +382,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do ) when object_type in ["Person", "Group", "Application", "Service", "Organization"] do with {:ok, %Actor{suspended: false} = old_actor} <- - ActivityPub.get_or_fetch_actor_by_url(object["id"]), + ActivityPubActor.get_or_fetch_actor_by_url(object["id"]), object_data <- object |> Converter.Actor.as_to_model_data(), {:ok, %Activity{} = activity, %Actor{} = new_actor} <- @@ -403,7 +405,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do ) do with actor <- Utils.get_actor(update_data), {:ok, %Actor{url: actor_url, suspended: false} = actor} <- - ActivityPub.get_or_fetch_actor_by_url(actor), + ActivityPubActor.get_or_fetch_actor_by_url(actor), {:ok, %Event{} = old_event} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(), object_data <- Converter.Event.as_to_model_data(object), @@ -428,7 +430,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do with actor <- Utils.get_actor(update_data), {:ok, %Actor{url: actor_url, suspended: false}} <- - ActivityPub.get_or_fetch_actor_by_url(actor), + ActivityPubActor.get_or_fetch_actor_by_url(actor), {:origin_check, true} <- {:origin_check, Utils.origin_check?(actor_url, update_data)}, object_data <- Converter.Comment.as_to_model_data(object), {:ok, old_entity} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(), @@ -448,7 +450,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do ) do with actor <- Utils.get_actor(update_data), {:ok, %Actor{url: actor_url, suspended: false} = actor} <- - ActivityPub.get_or_fetch_actor_by_url(actor), + ActivityPubActor.get_or_fetch_actor_by_url(actor), {:ok, %Post{} = old_post} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(), object_data <- Converter.Post.as_to_model_data(object), @@ -476,7 +478,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do when type in ["ResourceCollection", "Document"] do with actor <- Utils.get_actor(update_data), {:ok, %Actor{url: actor_url, suspended: false}} <- - ActivityPub.get_or_fetch_actor_by_url(actor), + ActivityPubActor.get_or_fetch_actor_by_url(actor), {:ok, %Resource{} = old_resource} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(), object_data <- Converter.Resource.as_to_model_data(object), @@ -501,7 +503,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do with actor <- Utils.get_actor(update_data), {:ok, %Actor{url: actor_url, suspended: false} = actor} <- - ActivityPub.get_or_fetch_actor_by_url(actor), + ActivityPubActor.get_or_fetch_actor_by_url(actor), {:origin_check, true} <- {:origin_check, Utils.origin_check?(actor_url, update_data)}, object_data <- Converter.Member.as_to_model_data(object), {:ok, old_entity} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(), @@ -543,7 +545,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do } = data ) do with actor <- Utils.get_actor(data), - {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(actor), + {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(actor), {:ok, object} <- fetch_obj_helper_as_activity_streams(object_id), {:ok, activity, object} <- ActivityPub.unannounce(actor, object, id, cancelled_activity_id, false) do @@ -561,8 +563,9 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do "id" => id } = _data ) do - with {:ok, %Actor{domain: nil} = followed} <- ActivityPub.get_or_fetch_actor_by_url(followed), - {:ok, %Actor{} = follower} <- ActivityPub.get_or_fetch_actor_by_url(follower), + with {:ok, %Actor{domain: nil} = followed} <- + ActivityPubActor.get_or_fetch_actor_by_url(followed), + {:ok, %Actor{} = follower} <- ActivityPubActor.get_or_fetch_actor_by_url(follower), {:ok, activity, object} <- ActivityPub.unfollow(follower, followed, id, false) do {:ok, activity, object} else @@ -579,7 +582,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do ) do with actor_url <- Utils.get_actor(data), {:actor, {:ok, %Actor{} = actor}} <- - {:actor, ActivityPub.get_or_fetch_actor_by_url(actor_url)}, + {:actor, ActivityPubActor.get_or_fetch_actor_by_url(actor_url)}, object_id <- Utils.get_url(object), {:ok, object} <- is_group_object_gone(object_id), {:origin_check, true} <- @@ -622,7 +625,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do when type in ["ResourceCollection", "Document"] do with actor <- Utils.get_actor(data), {:ok, %Actor{url: actor_url, suspended: false} = actor} <- - ActivityPub.get_or_fetch_actor_by_url(actor), + ActivityPubActor.get_or_fetch_actor_by_url(actor), {:ok, %Resource{} = old_resource} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(), object_data <- Converter.Resource.as_to_model_data(object), @@ -654,7 +657,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do ) do with actor <- Utils.get_actor(data), {:ok, %Actor{url: _actor_url, suspended: false} = actor} <- - ActivityPub.get_or_fetch_actor_by_url(actor), + ActivityPubActor.get_or_fetch_actor_by_url(actor), object <- Utils.get_url(object), {:ok, object} <- ActivityPub.fetch_object_from_url(object), {:ok, activity, object} <- @@ -672,7 +675,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do def handle_incoming(%{"type" => "Leave", "object" => object, "actor" => actor} = data) do with actor <- Utils.get_actor(data), - {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(actor), + {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(actor), object <- Utils.get_url(object), {:ok, object} <- ActivityPub.fetch_object_from_url(object), {:ok, activity, object} <- ActivityPub.leave(object, actor, false) do @@ -702,10 +705,10 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do Logger.info("Handle incoming to invite someone") with {:ok, %Actor{} = actor} <- - data |> Utils.get_actor() |> ActivityPub.get_or_fetch_actor_by_url(), + data |> Utils.get_actor() |> ActivityPubActor.get_or_fetch_actor_by_url(), {:ok, object} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(), {:ok, %Actor{} = target} <- - target |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url(), + target |> Utils.get_url() |> ActivityPubActor.get_or_fetch_actor_by_url(), {:ok, activity, %Member{} = member} <- ActivityPub.invite(object, actor, target, false, %{url: id}) do {:ok, activity, member} @@ -718,10 +721,10 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do Logger.info("Handle incoming to remove a member from a group") with {:ok, %Actor{id: moderator_id} = moderator} <- - data |> Utils.get_actor() |> ActivityPub.get_or_fetch_actor_by_url(), + data |> Utils.get_actor() |> ActivityPubActor.get_or_fetch_actor_by_url(), {:ok, person_id} <- get_remove_object(object), {:ok, %Actor{type: :Group, id: group_id} = group} <- - origin |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url(), + origin |> Utils.get_url() |> ActivityPubActor.get_or_fetch_actor_by_url(), {:is_admin, {:ok, %Member{role: role}}} when role in [:moderator, :administrator, :creator] <- {:is_admin, Actors.get_member(moderator_id, group_id)}, diff --git a/lib/federation/activity_pub/utils.ex b/lib/federation/activity_pub/utils.ex index d1cb0a3ed..01ad1abc6 100644 --- a/lib/federation/activity_pub/utils.ex +++ b/lib/federation/activity_pub/utils.ex @@ -14,6 +14,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.{Activity, Federator, Relay} + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Types.Ownable alias Mobilizon.Federation.ActivityStream.Converter alias Mobilizon.Federation.HTTPSignatures @@ -175,7 +176,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do @spec remote_actors(list(String.t())) :: list(Actor.t()) def remote_actors(recipients) do recipients - |> Enum.map(fn url -> ActivityPub.get_or_fetch_actor_by_url(url) end) + |> Enum.map(fn url -> ActivityPubActor.get_or_fetch_actor_by_url(url) end) |> Enum.map(fn {status, actor} -> case status do :ok -> @@ -259,8 +260,9 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do are_same_origin?(id, actor) end - def origin_check?(_id, %{"type" => type} = _params) when type in ["Actor", "Person", "Group"], - do: true + def origin_check?(id, %{"type" => type, "id" => actor_id} = _params) + when type in ["Actor", "Person", "Group"], + do: id == actor_id def origin_check?(_id, %{"actor" => nil} = _args), do: false diff --git a/lib/federation/activity_stream/converter/discussion.ex b/lib/federation/activity_stream/converter/discussion.ex index abc4196bd..2029923d6 100644 --- a/lib/federation/activity_stream/converter/discussion.ex +++ b/lib/federation/activity_stream/converter/discussion.ex @@ -8,7 +8,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Discussion do alias Mobilizon.Actors.Actor alias Mobilizon.Discussions.Discussion - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Federation.ActivityStream.Converter.Discussion, as: DiscussionConverter alias Mobilizon.Storage.Repo @@ -49,10 +49,10 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Discussion do def as_to_model_data(%{"type" => "Note", "name" => name} = object) when not is_nil(name) do with creator_url <- Map.get(object, "actor"), {:ok, %Actor{id: creator_id, suspended: false}} <- - ActivityPub.get_or_fetch_actor_by_url(creator_url), + ActivityPubActor.get_or_fetch_actor_by_url(creator_url), actor_url <- Map.get(object, "attributedTo"), {:ok, %Actor{id: actor_id, suspended: false}} <- - ActivityPub.get_or_fetch_actor_by_url(actor_url) do + ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do %{ title: name, actor_id: actor_id, diff --git a/lib/federation/activity_stream/converter/flag.ex b/lib/federation/activity_stream/converter/flag.ex index 975b78a84..290551e02 100644 --- a/lib/federation/activity_stream/converter/flag.ex +++ b/lib/federation/activity_stream/converter/flag.ex @@ -14,7 +14,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do alias Mobilizon.Events.Event alias Mobilizon.Reports.Report - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Relay alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} @@ -65,10 +65,11 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do @spec as_to_model(map) :: map def as_to_model(%{"object" => objects} = object) do - with {:ok, %Actor{} = reporter} <- ActivityPub.get_or_fetch_actor_by_url(object["actor"]), + with {:ok, %Actor{} = reporter} <- + ActivityPubActor.get_or_fetch_actor_by_url(object["actor"]), %Actor{} = reported <- Enum.reduce_while(objects, nil, fn url, _ -> - case ActivityPub.get_or_fetch_actor_by_url(url) do + case ActivityPubActor.get_or_fetch_actor_by_url(url) do {:ok, %Actor{} = actor} -> {:halt, actor} diff --git a/lib/federation/activity_stream/converter/member.ex b/lib/federation/activity_stream/converter/member.ex index 0d55e8ef1..be4db50a5 100644 --- a/lib/federation/activity_stream/converter/member.ex +++ b/lib/federation/activity_stream/converter/member.ex @@ -8,7 +8,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Member do alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Member, as: MemberModel - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Utils alias Mobilizon.Federation.ActivityStream.Convertible @@ -53,5 +53,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Member do @spec get_actor(String.t() | map() | nil) :: {:ok, Actor.t()} | {:error, String.t()} defp get_actor(nil), do: {:error, "nil property found for actor data"} - defp get_actor(actor), do: actor |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url() + + defp get_actor(actor), + do: actor |> Utils.get_url() |> ActivityPubActor.get_or_fetch_actor_by_url() end diff --git a/lib/federation/activity_stream/converter/post.ex b/lib/federation/activity_stream/converter/post.ex index 020fda4aa..f8d23c7ce 100644 --- a/lib/federation/activity_stream/converter/post.ex +++ b/lib/federation/activity_stream/converter/post.ex @@ -6,7 +6,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do internal one, and back. """ alias Mobilizon.Actors.Actor - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.{Audience, Utils} alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Federation.ActivityStream.Converter.Media, as: MediaConverter @@ -91,7 +91,9 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do @spec get_actor(String.t() | map() | nil) :: {:ok, Actor.t()} | {:error, String.t()} defp get_actor(nil), do: {:error, "nil property found for actor data"} - defp get_actor(actor), do: actor |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url() + + defp get_actor(actor), + do: actor |> Utils.get_url() |> ActivityPubActor.get_or_fetch_actor_by_url() defp to_date(nil), do: nil defp to_date(%DateTime{} = date), do: DateTime.to_iso8601(date) diff --git a/lib/federation/activity_stream/converter/resource.ex b/lib/federation/activity_stream/converter/resource.ex index 7c045f256..0472ca1b0 100644 --- a/lib/federation/activity_stream/converter/resource.ex +++ b/lib/federation/activity_stream/converter/resource.ex @@ -7,6 +7,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Resource do """ alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Utils alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Resources @@ -88,7 +89,9 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Resource do @spec get_actor(String.t() | map() | nil) :: {:ok, Actor.t()} | {:error, String.t()} defp get_actor(nil), do: {:error, "nil property found for actor data"} - defp get_actor(actor), do: actor |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url() + + defp get_actor(actor), + do: actor |> Utils.get_url() |> ActivityPubActor.get_or_fetch_actor_by_url() defp get_context(%Resource{parent_id: nil, actor: %Actor{resources_url: resources_url}}), do: resources_url diff --git a/lib/federation/activity_stream/converter/todo.ex b/lib/federation/activity_stream/converter/todo.ex index fad2d8a53..fdd6f38c8 100644 --- a/lib/federation/activity_stream/converter/todo.ex +++ b/lib/federation/activity_stream/converter/todo.ex @@ -7,6 +7,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Todo do """ alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Todos alias Mobilizon.Todos.{Todo, TodoList} @@ -51,7 +52,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Todo do %{"type" => "Todo", "actor" => actor_url, "todoList" => todo_list_url} = object ) do with {:ok, %Actor{id: creator_id} = _creator} <- - ActivityPub.get_or_fetch_actor_by_url(actor_url), + ActivityPubActor.get_or_fetch_actor_by_url(actor_url), {:todo_list, %TodoList{id: todo_list_id}} <- {:todo_list, Todos.get_todo_list_by_url(todo_list_url)} do %{ diff --git a/lib/federation/activity_stream/converter/todo_list.ex b/lib/federation/activity_stream/converter/todo_list.ex index 657e97df3..76ad46fc7 100644 --- a/lib/federation/activity_stream/converter/todo_list.ex +++ b/lib/federation/activity_stream/converter/todo_list.ex @@ -6,7 +6,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.TodoList do internal one, and back. """ alias Mobilizon.Actors.Actor - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Todos.TodoList @@ -39,7 +39,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.TodoList do @impl Converter @spec as_to_model_data(map) :: {:ok, map} | {:error, any()} def as_to_model_data(%{"type" => "TodoList", "actor" => actor_url} = object) do - case ActivityPub.get_or_fetch_actor_by_url(actor_url) do + case ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do {:ok, %Actor{type: :Group, id: group_id} = _group} -> %{ title: object["name"], diff --git a/lib/federation/activity_stream/converter/utils.ex b/lib/federation/activity_stream/converter/utils.ex index 79c604a63..a78456bd2 100644 --- a/lib/federation/activity_stream/converter/utils.ex +++ b/lib/federation/activity_stream/converter/utils.ex @@ -10,7 +10,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do alias Mobilizon.Mention alias Mobilizon.Storage.Repo - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityStream.Converter.Media, as: MediaConverter alias Mobilizon.Web.Endpoint @@ -114,7 +114,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do @spec create_mention(map(), list()) :: list() defp create_mention(mention, acc) when is_map(mention) do with true <- mention["type"] == "Mention", - {:ok, %Actor{id: actor_id}} <- ActivityPub.get_or_fetch_actor_by_url(mention["href"]) do + {:ok, %Actor{id: actor_id}} <- + ActivityPubActor.get_or_fetch_actor_by_url(mention["href"]) do acc ++ [%{actor_id: actor_id}] else _err -> @@ -169,7 +170,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do @spec fetch_actor(String.t()) :: Actor.t() defp fetch_actor(actor_url) do with {:ok, %Actor{suspended: false} = actor} <- - ActivityPub.get_or_fetch_actor_by_url(actor_url) do + ActivityPubActor.get_or_fetch_actor_by_url(actor_url) do actor end end diff --git a/lib/federation/http_signatures/signature.ex b/lib/federation/http_signatures/signature.ex index 787b4ccb4..ff703a3ed 100644 --- a/lib/federation/http_signatures/signature.ex +++ b/lib/federation/http_signatures/signature.ex @@ -12,7 +12,7 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do alias Mobilizon.Actors.Actor - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor require Logger @@ -53,7 +53,7 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do {:ok, String.t()} | {:error, :actor_fetch_error | :pem_decode_error | :actor_not_fetchable} defp get_public_key_for_url(url) do - with {:ok, %Actor{keys: keys}} <- ActivityPub.get_or_fetch_actor_by_url(url), + with {:ok, %Actor{keys: keys}} <- ActivityPubActor.get_or_fetch_actor_by_url(url), {:ok, public_key} <- prepare_public_key(keys) do {:ok, public_key} else @@ -90,7 +90,7 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), actor_id <- key_id_to_actor_url(kid), :ok <- Logger.debug("Refetching public key for #{actor_id}"), - {:ok, _actor} <- ActivityPub.make_actor_from_url(actor_id), + {:ok, _actor} <- ActivityPubActor.make_actor_from_url(actor_id), {:ok, public_key} <- get_public_key_for_url(actor_id) do {:ok, public_key} end diff --git a/lib/federation/web_finger/web_finger.ex b/lib/federation/web_finger/web_finger.ex index fbde57e4b..409f67b8f 100644 --- a/lib/federation/web_finger/web_finger.ex +++ b/lib/federation/web_finger/web_finger.ex @@ -10,7 +10,7 @@ defmodule Mobilizon.Federation.WebFinger do alias Mobilizon.Actors alias Mobilizon.Actors.Actor - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.WebFinger.XmlBuilder alias Mobilizon.Service.HTTP.{HostMetaClient, WebfingerClient} alias Mobilizon.Web.Endpoint @@ -56,7 +56,7 @@ defmodule Mobilizon.Federation.WebFinger do {:ok, represent_actor(actor, "JSON")} else _e -> - case ActivityPub.get_or_fetch_actor_by_url(resource) do + case ActivityPubActor.get_or_fetch_actor_by_url(resource) do {:ok, %Actor{} = actor} when not is_nil(actor) -> {:ok, represent_actor(actor, "JSON")} diff --git a/lib/graphql/api/search.ex b/lib/graphql/api/search.ex index f0f1ec83c..d652dcb68 100644 --- a/lib/graphql/api/search.ex +++ b/lib/graphql/api/search.ex @@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.API.Search do alias Mobilizon.Storage.Page alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor require Logger @@ -92,7 +93,7 @@ defmodule Mobilizon.GraphQL.API.Search do # If the search string is an username @spec process_from_username(String.t()) :: Page.t() defp process_from_username(search) do - case ActivityPub.find_or_make_actor_from_nickname(search) do + case ActivityPubActor.find_or_make_actor_from_nickname(search) do {:ok, actor} -> %Page{total: 1, elements: [actor]} diff --git a/lib/graphql/resolvers/group.ex b/lib/graphql/resolvers/group.ex index c31892ca7..e5ed6a2f3 100644 --- a/lib/graphql/resolvers/group.ex +++ b/lib/graphql/resolvers/group.ex @@ -7,6 +7,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do alias Mobilizon.{Actors, Events, Users} alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.GraphQL.API alias Mobilizon.Users.User alias Mobilizon.Web.Upload @@ -27,7 +28,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do } ) do with {:group, {:ok, %Actor{id: group_id, suspended: false} = group}} <- - {:group, ActivityPub.find_or_make_group_from_nickname(name)}, + {:group, ActivityPubActor.find_or_make_group_from_nickname(name)}, {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)} do {:ok, group} @@ -45,7 +46,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do def find_group(_parent, %{preferred_username: name}, _resolution) do with {:ok, %Actor{suspended: false} = actor} <- - ActivityPub.find_or_make_group_from_nickname(name), + ActivityPubActor.find_or_make_group_from_nickname(name), %Actor{} = actor <- restrict_fields_for_non_member_request(actor) do {:ok, actor} else diff --git a/lib/graphql/resolvers/member.ex b/lib/graphql/resolvers/member.ex index 3e20194a9..8af57bba5 100644 --- a/lib/graphql/resolvers/member.ex +++ b/lib/graphql/resolvers/member.ex @@ -7,6 +7,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do alias Mobilizon.{Actors, Users} alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Refresher alias Mobilizon.Storage.Page alias Mobilizon.Users.User @@ -70,7 +71,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do target_actor_username |> String.trim() |> String.trim_leading("@"), {:target_actor_username, {:ok, %Actor{id: target_actor_id} = target_actor}} <- {:target_actor_username, - ActivityPub.find_or_make_actor_from_nickname(target_actor_username)}, + ActivityPubActor.find_or_make_actor_from_nickname(target_actor_username)}, {:existant, true} <- {:existant, check_member_not_existant_or_rejected(target_actor_id, group.id)}, {:ok, _activity, %Member{} = member} <- ActivityPub.invite(group, actor, target_actor) do diff --git a/lib/graphql/resolvers/person.ex b/lib/graphql/resolvers/person.ex index 70c720a81..06b898531 100644 --- a/lib/graphql/resolvers/person.ex +++ b/lib/graphql/resolvers/person.ex @@ -13,6 +13,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do import Mobilizon.Web.Gettext alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor require Logger alias Mobilizon.Web.Upload @@ -39,7 +40,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do context: %{current_user: %User{} = user} }) do with {:ok, %Actor{id: actor_id} = actor} <- - ActivityPub.find_or_make_actor_from_nickname(preferred_username), + ActivityPubActor.find_or_make_actor_from_nickname(preferred_username), {:own, {:is_owned, _}} <- {:own, User.owns_actor(user, actor_id)} do {:ok, actor} else diff --git a/lib/mix/tasks/mobilizon/actors/refresh.ex b/lib/mix/tasks/mobilizon/actors/refresh.ex index 44bca92e7..893af5f2b 100644 --- a/lib/mix/tasks/mobilizon/actors/refresh.ex +++ b/lib/mix/tasks/mobilizon/actors/refresh.ex @@ -4,7 +4,7 @@ defmodule Mix.Tasks.Mobilizon.Actors.Refresh do """ use Mix.Task alias Mobilizon.Actors.Actor - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Storage.Repo import Ecto.Query import Mix.Tasks.Mobilizon.Common @@ -65,7 +65,7 @@ defmodule Mix.Tasks.Mobilizon.Actors.Refresh do def run([preferred_username]) do start_mobilizon() - case ActivityPub.make_actor_from_nickname(preferred_username) do + case ActivityPubActor.make_actor_from_nickname(preferred_username) do {:ok, %Actor{}} -> shell_info(""" Actor #{preferred_username} refreshed @@ -89,7 +89,7 @@ defmodule Mix.Tasks.Mobilizon.Actors.Refresh do @spec make_actor(String.t(), boolean()) :: any() defp make_actor(username, verbose) do - ActivityPub.make_actor_from_nickname(username) + ActivityPubActor.make_actor_from_nickname(username) rescue _ -> if verbose do diff --git a/lib/web/controllers/activity_pub_controller.ex b/lib/web/controllers/activity_pub_controller.ex index 909369976..d8609359a 100644 --- a/lib/web/controllers/activity_pub_controller.ex +++ b/lib/web/controllers/activity_pub_controller.ex @@ -10,6 +10,7 @@ defmodule Mobilizon.Web.ActivityPubController do alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.{Federator, Utils} alias Mobilizon.Web.ActivityPub.ActorView @@ -108,7 +109,7 @@ defmodule Mobilizon.Web.ActivityPubController do def inbox(%{assigns: %{valid_signature: true}} = conn, %{"name" => preferred_username} = params) do with %Actor{url: recipient_url} = recipient <- Actors.get_local_actor_by_name(preferred_username), - {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(params["actor"]), + {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(params["actor"]), true <- Utils.recipient_in_message(recipient, actor, params), params <- Utils.maybe_splice_recipient(recipient_url, params) do Federator.enqueue(:incoming_ap_doc, params) diff --git a/lib/web/plugs/mapped_signature_to_identity.ex b/lib/web/plugs/mapped_signature_to_identity.ex index 84262bc3f..548f3ac0c 100644 --- a/lib/web/plugs/mapped_signature_to_identity.ex +++ b/lib/web/plugs/mapped_signature_to_identity.ex @@ -12,7 +12,7 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentity do alias Mobilizon.Actors.Actor - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Utils alias Mobilizon.Federation.HTTPSignatures.Signature @@ -34,7 +34,7 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentity do @spec actor_from_key_id(Plug.Conn.t()) :: Actor.t() | nil defp actor_from_key_id(conn) do with key_actor_id when is_binary(key_actor_id) <- key_id_from_conn(conn), - {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(key_actor_id) do + {:ok, %Actor{} = actor} <- ActivityPubActor.get_or_fetch_actor_by_url(key_actor_id) do actor else _ -> diff --git a/test/federation/activity_pub/activity_pub_test.exs b/test/federation/activity_pub/activity_pub_test.exs index ac4f697bf..776ba7e23 100644 --- a/test/federation/activity_pub/activity_pub_test.exs +++ b/test/federation/activity_pub/activity_pub_test.exs @@ -11,13 +11,12 @@ defmodule Mobilizon.Federation.ActivityPubTest do import Mox import Mobilizon.Factory - alias Mobilizon.{Actors, Discussions, Events} - alias Mobilizon.Actors.Actor + alias Mobilizon.{Discussions, Events} alias Mobilizon.Resources.Resource alias Mobilizon.Todos.{Todo, TodoList} alias Mobilizon.Federation.ActivityPub - alias Mobilizon.Federation.ActivityPub.{Relay, Utils} + alias Mobilizon.Federation.ActivityPub.Utils alias Mobilizon.Federation.HTTPSignatures.Signature alias Mobilizon.Service.HTTP.ActivityPub.Mock @@ -40,116 +39,6 @@ defmodule Mobilizon.Federation.ActivityPubTest do end end - describe "fetching actor from its url" do - test "returns an actor from nickname" do - use_cassette "activity_pub/fetch_tcit@framapiaf.org" do - assert {:ok, - %Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :public} = - _actor} = ActivityPub.make_actor_from_nickname("tcit@framapiaf.org") - end - - use_cassette "activity_pub/fetch_tcit@framapiaf.org_not_discoverable" do - assert {:ok, - %Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted} = - _actor} = ActivityPub.make_actor_from_nickname("tcit@framapiaf.org") - end - end - - @actor_url "https://framapiaf.org/users/tcit" - test "returns an actor from url" do - # Initial fetch - use_cassette "activity_pub/fetch_framapiaf.org_users_tcit" do - # Unlisted because discoverable is not present in the JSON payload - assert {:ok, - %Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted}} = - ActivityPub.get_or_fetch_actor_by_url(@actor_url) - end - - # Fetch uses cache if Actors.needs_update? returns false - with_mocks([ - {Actors, [:passthrough], - [ - get_actor_by_url: fn @actor_url, false -> - {:ok, - %Actor{ - preferred_username: "tcit", - domain: "framapiaf.org" - }} - end, - needs_update?: fn _ -> false end - ]}, - {ActivityPub, [:passthrough], - make_actor_from_url: fn @actor_url, false -> - {:ok, - %Actor{ - preferred_username: "tcit", - domain: "framapiaf.org" - }} - end} - ]) do - assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"}} = - ActivityPub.get_or_fetch_actor_by_url(@actor_url) - - assert_called(Actors.needs_update?(:_)) - refute called(ActivityPub.make_actor_from_url(@actor_url, false)) - end - - # Fetch doesn't use cache if Actors.needs_update? returns true - with_mocks([ - {Actors, [:passthrough], - [ - get_actor_by_url: fn @actor_url, false -> - {:ok, - %Actor{ - preferred_username: "tcit", - domain: "framapiaf.org" - }} - end, - needs_update?: fn _ -> true end - ]}, - {ActivityPub, [:passthrough], - make_actor_from_url: fn @actor_url, false -> - {:ok, - %Actor{ - preferred_username: "tcit", - domain: "framapiaf.org" - }} - end} - ]) do - assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"}} = - ActivityPub.get_or_fetch_actor_by_url(@actor_url) - - assert_called(ActivityPub.get_or_fetch_actor_by_url(@actor_url)) - assert_called(Actors.get_actor_by_url(@actor_url, false)) - assert_called(Actors.needs_update?(:_)) - assert_called(ActivityPub.make_actor_from_url(@actor_url, false)) - end - end - - @public_url "https://www.w3.org/ns/activitystreams#Public" - test "activitystreams#Public uri returns Relay actor" do - assert ActivityPub.get_or_fetch_actor_by_url(@public_url) == {:ok, Relay.get_actor()} - end - end - - describe "create activities" do - # test "removes doubled 'to' recipients" do - # actor = insert(:actor) - # - # {:ok, activity, _} = - # ActivityPub.create(%{ - # to: ["user1", "user1", "user2"], - # actor: actor, - # context: "", - # object: %{} - # }) - # - # assert activity.data["to"] == ["user1", "user2"] - # assert activity.actor == actor.url - # assert activity.recipients == ["user1", "user2"] - # end - end - describe "fetching an" do test "object by url" do url = "https://framapiaf.org/users/Framasoft/statuses/102093631881522097" diff --git a/test/federation/activity_pub/actor_test.exs b/test/federation/activity_pub/actor_test.exs new file mode 100644 index 000000000..69ebc5daa --- /dev/null +++ b/test/federation/activity_pub/actor_test.exs @@ -0,0 +1,120 @@ +defmodule Mobilizon.Federation.ActivityPub.ActorTest do + use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney + use Mobilizon.DataCase + + import Mock + + alias Mobilizon.Actors + alias Mobilizon.Actors.Actor + + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor + alias Mobilizon.Federation.ActivityPub.{Fetcher, Relay} + + describe "fetching actor from its url" do + test "returns an actor from nickname" do + use_cassette "activity_pub/fetch_tcit@framapiaf.org" do + assert {:ok, + %Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :public} = + _actor} = ActivityPubActor.make_actor_from_nickname("tcit@framapiaf.org") + end + + use_cassette "activity_pub/fetch_tcit@framapiaf.org_not_discoverable" do + assert {:ok, + %Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted} = + _actor} = ActivityPubActor.make_actor_from_nickname("tcit@framapiaf.org") + end + end + + @actor_url "https://framapiaf.org/users/tcit" + test "returns an actor from url" do + # Initial fetch + use_cassette "activity_pub/fetch_framapiaf.org_users_tcit" do + # Unlisted because discoverable is not present in the JSON payload + assert {:ok, + %Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted}} = + ActivityPubActor.get_or_fetch_actor_by_url(@actor_url) + end + + # Fetch uses cache if Actors.needs_update? returns false + with_mocks([ + {Actors, [:passthrough], + [ + get_actor_by_url: fn @actor_url, false -> + {:ok, + %Actor{ + preferred_username: "tcit", + domain: "framapiaf.org" + }} + end, + needs_update?: fn _ -> false end + ]}, + {ActivityPubActor, [:passthrough], + make_actor_from_url: fn @actor_url, false -> + {:ok, + %Actor{ + preferred_username: "tcit", + domain: "framapiaf.org" + }} + end} + ]) do + assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"}} = + ActivityPubActor.get_or_fetch_actor_by_url(@actor_url) + + assert_called(Actors.needs_update?(:_)) + refute called(ActivityPubActor.make_actor_from_url(@actor_url, false)) + end + + # Fetch doesn't use cache if Actors.needs_update? returns true + with_mocks([ + {Actors, [:passthrough], + [ + get_actor_by_url: fn @actor_url, false -> + {:ok, + %Actor{ + preferred_username: "tcit", + domain: "framapiaf.org" + }} + end, + needs_update?: fn _ -> true end + ]}, + {ActivityPubActor, [:passthrough], + make_actor_from_url: fn @actor_url, false -> + {:ok, + %Actor{ + preferred_username: "tcit", + domain: "framapiaf.org" + }} + end} + ]) do + assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"}} = + ActivityPubActor.get_or_fetch_actor_by_url(@actor_url) + + assert_called(ActivityPubActor.get_or_fetch_actor_by_url(@actor_url)) + assert_called(Actors.get_actor_by_url(@actor_url, false)) + assert_called(Actors.needs_update?(:_)) + assert_called(ActivityPubActor.make_actor_from_url(@actor_url, false)) + end + end + + test "handles remote actor being deleted" do + with_mocks([ + {Fetcher, [:passthrough], + fetch_and_prepare_actor_from_url: fn @actor_url -> + {:error, :actor_deleted} + end} + ]) do + assert match?( + {:error, :actor_deleted}, + ActivityPubActor.make_actor_from_url(@actor_url, false) + ) + + assert_called(Fetcher.fetch_and_prepare_actor_from_url(@actor_url)) + end + end + + @public_url "https://www.w3.org/ns/activitystreams#Public" + test "activitystreams#Public uri returns Relay actor" do + assert ActivityPubActor.get_or_fetch_actor_by_url(@public_url) == {:ok, Relay.get_actor()} + end + end +end diff --git a/test/federation/activity_pub/transmogrifier/delete_test.exs b/test/federation/activity_pub/transmogrifier/delete_test.exs index 30717266a..cefd4d64f 100644 --- a/test/federation/activity_pub/transmogrifier/delete_test.exs +++ b/test/federation/activity_pub/transmogrifier/delete_test.exs @@ -3,7 +3,6 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney use Oban.Testing, repo: Mobilizon.Storage.Repo import Mobilizon.Factory - import ExUnit.CaptureLog import Mox alias Mobilizon.{Actors, Discussions, Events, Posts, Resources} @@ -78,7 +77,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do data |> Map.put("object", object) - :error = Transmogrifier.handle_incoming(data) + {:error, :unknown_actor} = Transmogrifier.handle_incoming(data) assert Discussions.get_comment_from_url(comment.url) end @@ -119,13 +118,14 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do test "it fails for incoming actor deletes with spoofed origin" do %{url: url} = insert(:actor) - deleted_actor_url = "https://framapiaf.org/users/admin" data = File.read!("test/fixtures/mastodon-delete-user.json") |> Jason.decode!() |> Map.put("actor", url) + deleted_actor_url = "https://framapiaf.org/users/admin" + deleted_actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!() @@ -137,9 +137,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do {:ok, %Tesla.Env{status: 200, body: deleted_actor_data}} end) - assert capture_log(fn -> - assert :error == Transmogrifier.handle_incoming(data) - end) =~ "Object origin check failed" + assert :error == Transmogrifier.handle_incoming(data) assert Actors.get_actor_by_url(url) end diff --git a/test/federation/activity_pub/transmogrifier/posts_test.exs b/test/federation/activity_pub/transmogrifier/posts_test.exs index 2ccbb4fe2..c79b01a0a 100644 --- a/test/federation/activity_pub/transmogrifier/posts_test.exs +++ b/test/federation/activity_pub/transmogrifier/posts_test.exs @@ -7,7 +7,6 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.PostsTest do alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier} alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Posts.Post - alias Mobilizon.Service.HTTP.ActivityPub.Mock describe "handle incoming posts" do setup :verify_on_exit! diff --git a/test/federation/activity_pub/transmogrifier_test.exs b/test/federation/activity_pub/transmogrifier_test.exs index f04ad5a77..d344b5633 100644 --- a/test/federation/activity_pub/transmogrifier_test.exs +++ b/test/federation/activity_pub/transmogrifier_test.exs @@ -21,6 +21,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do alias Mobilizon.Todos.{Todo, TodoList} alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Federation.ActivityPub.Utils alias Mobilizon.Federation.ActivityPub.{Activity, Relay, Transmogrifier} alias Mobilizon.Federation.ActivityStream.Convertible @@ -89,7 +90,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do preferred_username: "member" ) - with_mock ActivityPub, [:passthrough], + with_mock ActivityPubActor, [:passthrough], get_or_fetch_actor_by_url: fn url -> case url do ^group_url -> {:ok, group} @@ -168,7 +169,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do group = insert(:group, domain: "morebilizon.com", url: @mobilizon_group_url) %Actor{url: actor_url} = actor = insert(:actor) - with_mock ActivityPub, [:passthrough], + with_mock ActivityPubActor, [:passthrough], get_or_fetch_actor_by_url: fn url -> case url do @mobilizon_group_url -> {:ok, group} @@ -198,7 +199,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do test "it accepts incoming todo lists and handles group being not found" do %Actor{url: actor_url} = actor = insert(:actor) - with_mock ActivityPub, [:passthrough], + with_mock ActivityPubActor, [:passthrough], get_or_fetch_actor_by_url: fn url -> case url do @mobilizon_group_url -> {:error, "Not found"} @@ -274,7 +275,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do group = insert(:group, domain: "morebilizon.com", url: @mobilizon_group_url) %Actor{url: actor_url} = actor = insert(:actor) - with_mock ActivityPub, [:passthrough], + with_mock ActivityPubActor, [:passthrough], get_or_fetch_actor_by_url: fn url -> case url do @mobilizon_group_url -> {:ok, group} @@ -304,7 +305,7 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do test "it accepts incoming todo lists and handles group being not found" do %Actor{url: actor_url} = actor = insert(:actor) - with_mock ActivityPub, [:passthrough], + with_mock ActivityPubActor, [:passthrough], get_or_fetch_actor_by_url: fn url -> case url do @mobilizon_group_url -> {:error, "Not found"} diff --git a/test/graphql/api/search_test.exs b/test/graphql/api/search_test.exs index 74e8a65ea..2d0b1351f 100644 --- a/test/graphql/api/search_test.exs +++ b/test/graphql/api/search_test.exs @@ -12,14 +12,15 @@ defmodule Mobilizon.GraphQL.API.SearchTest do alias Mobilizon.GraphQL.API.Search alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor test "search an user by username" do - with_mock ActivityPub, + with_mock ActivityPubActor, find_or_make_actor_from_nickname: fn "toto@domain.tld" -> {:ok, %Actor{id: 42}} end do assert {:ok, %Page{total: 1, elements: [%Actor{id: 42}]}} == Search.search_actors(%{term: "toto@domain.tld"}, 1, 10, :Person) - assert_called(ActivityPub.find_or_make_actor_from_nickname("toto@domain.tld")) + assert_called(ActivityPubActor.find_or_make_actor_from_nickname("toto@domain.tld")) end end diff --git a/test/mobilizon/actors/actors_test.exs b/test/mobilizon/actors/actors_test.exs index ca4ef2ba7..94abd067e 100644 --- a/test/mobilizon/actors/actors_test.exs +++ b/test/mobilizon/actors/actors_test.exs @@ -13,7 +13,7 @@ defmodule Mobilizon.ActorsTest do alias Mobilizon.Service.Workers alias Mobilizon.Storage.Page - alias Mobilizon.Federation.ActivityPub + alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor alias Mobilizon.Web.Upload.Uploader @@ -106,7 +106,7 @@ defmodule Mobilizon.ActorsTest do preferred_username: preferred_username, domain: domain, avatar: %FileModel{name: picture_name} = _picture - } = _actor} = ActivityPub.get_or_fetch_actor_by_url(@remote_account_url) + } = _actor} = ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url) assert picture_name == "a28c50ce5f2b13fd.jpg" @@ -156,7 +156,8 @@ defmodule Mobilizon.ActorsTest do test "get_actor_by_name_with_preload!/1 returns the remote actor with its organized events" do use_cassette "actors/remote_actor_mastodon_tcit" do - with {:ok, %Actor{} = actor} <- ActivityPub.get_or_fetch_actor_by_url(@remote_account_url) do + with {:ok, %Actor{} = actor} <- + ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url) do assert Actors.get_actor_by_name_with_preload( "#{actor.preferred_username}@#{actor.domain}" ).organized_events == [] @@ -186,7 +187,7 @@ defmodule Mobilizon.ActorsTest do %{actor: %Actor{id: actor_id}} do use_cassette "actors/remote_actor_mastodon_tcit" do with {:ok, %Actor{id: actor2_id}} <- - ActivityPub.get_or_fetch_actor_by_url(@remote_account_url) do + ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url) do %Page{total: 2, elements: actors} = Actors.build_actors_by_username_or_name_page("tcit", actor_type: [:Person], From 87aeac6aea6bbbb169ecdeb2560fe3af09d82f4c Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 23 Apr 2021 09:03:19 +0200 Subject: [PATCH 04/31] Remove duplicate text in emails Signed-off-by: Thomas Citharel --- lib/web/templates/email/email_changed_old.html.eex | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/web/templates/email/email_changed_old.html.eex b/lib/web/templates/email/email_changed_old.html.eex index a4c2acd0c..ab5ea3e7e 100644 --- a/lib/web/templates/email/email_changed_old.html.eex +++ b/lib/web/templates/email/email_changed_old.html.eex @@ -37,9 +37,6 @@

<%= gettext("The email address for your account on %{host} is being changed to:", host: @instance[:name]) |> raw %>

-

- <%= gettext("If you did not trigger this change yourself, it is likely that someone has gained access to your %{host} account. Please log in and change your password immediately. If you cannot login, contact the admin on %{host}.", host: @instance[:name]) %> -

From 96b57811816ce18a8d012a7a6d73c4efddf950b0 Mon Sep 17 00:00:00 2001 From: deadmorose Date: Thu, 22 Apr 2021 15:25:58 +0000 Subject: [PATCH 05/31] Translated using Weblate (Russian) Currently translated at 100.0% (987 of 987 strings) Translation: Mobilizon/Frontend Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/ru/ --- js/src/i18n/ru.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/i18n/ru.json b/js/src/i18n/ru.json index 12ffa3d90..875b59a91 100644 --- a/js/src/i18n/ru.json +++ b/js/src/i18n/ru.json @@ -122,7 +122,6 @@ "Click to upload": "Нажмите, чтобы загрузить", "Close": "Закрыть", "Close comments for all (except for admins)": "Закрыть комментарии для всех (кроме админов)", - "Events nearby": "Ближайшие мероприятия", "Closed": "Закрыто", "Comment deleted": "Комментарий удален", "Comment from @{username} reported": "Жалоба на комментарий от @{username} отправлена", @@ -255,6 +254,7 @@ "Event {eventTitle} deleted": "Мероприятие {eventTitle} удалено", "Event {eventTitle} reported": "Жалоба на мероприятие {eventTitle} отправлена", "Events": "Мероприятия", + "Events nearby": "Ближайшие мероприятия", "Events tagged with {tag}": "События с тегом {tag}", "Everything": "Всё", "Ex: mobilizon.fr": "Например: mobilizon.fr", @@ -342,6 +342,7 @@ "Instance Terms URL": "URL условий использования узла", "Instance administrator": "Администратор узла", "Instance configuration": "Настройки узла", + "Instance feeds": "Ленты узла", "Instance languages": "Языки узла", "Instance rules": "Правила узла", "Instance settings": "Настройки узла", From eaadf261ac9f3befdfd1872e11adec43e3b41a72 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 23 Apr 2021 09:25:57 +0200 Subject: [PATCH 06/31] Handle actor fetch issues better Signed-off-by: Thomas Citharel --- lib/federation/activity_pub/fetcher.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/federation/activity_pub/fetcher.ex b/lib/federation/activity_pub/fetcher.ex index 7d1b73d63..019e0237e 100644 --- a/lib/federation/activity_pub/fetcher.ex +++ b/lib/federation/activity_pub/fetcher.ex @@ -119,6 +119,10 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do Logger.info("Response HTTP 410") {:error, :actor_deleted} + {:ok, %Tesla.Env{}} -> + Logger.info("Non 200 HTTP Code") + {:error, :http_error} + {:error, e} -> Logger.warn("Could not decode actor at fetch #{url}, #{inspect(e)}") {:error, e} From 2d0abaad4a11c48d2d42f03c92bf6980ca6681e2 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 23 Apr 2021 09:26:16 +0200 Subject: [PATCH 07/31] Handle rendering AP issues Signed-off-by: Thomas Citharel --- lib/web/views/error_view.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/web/views/error_view.ex b/lib/web/views/error_view.ex index 4cbc238a2..8d4ba7780 100644 --- a/lib/web/views/error_view.ex +++ b/lib/web/views/error_view.ex @@ -51,6 +51,10 @@ defmodule Mobilizon.Web.ErrorView do render("500.html", assigns) end + def render("500.activity-json", assigns) do + render("500.html", assigns) + end + def render("500.html", assigns) do Mobilizon.Config.instance_config() |> Keyword.get(:default_language, "en") From 687d1685f088970274dcf37daf268eb007cfc1f1 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 23 Apr 2021 09:57:23 +0200 Subject: [PATCH 08/31] Fix metadata remote image URL Signed-off-by: Thomas Citharel --- lib/service/rich_media/parser.ex | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/service/rich_media/parser.ex b/lib/service/rich_media/parser.ex index 2ea91f995..1d45b53d5 100644 --- a/lib/service/rich_media/parser.ex +++ b/lib/service/rich_media/parser.ex @@ -208,7 +208,7 @@ defmodule Mobilizon.Service.RichMedia.Parser do defp check_parsed_data(%{title: title} = data) when is_binary(title) and byte_size(title) > 0 do - {:ok, data} + data end defp check_parsed_data(data) do @@ -285,15 +285,20 @@ defmodule Mobilizon.Service.RichMedia.Parser do image_remote_url = cond do - is_nil(image_uri.host) -> "#{uri.scheme}://#{uri.host}#{image_remote_url}" + is_nil(image_uri.host) -> "#{uri.scheme}://#{uri.host}#{correct_path(image_remote_url)}" is_nil(image_uri.scheme) -> "#{uri.scheme}:#{image_remote_url}" true -> image_remote_url end - Map.put(data, :image_remote_url, image_remote_url) + data = Map.put(data, :image_remote_url, image_remote_url) + {:ok, data} end - defp check_remote_picture_path(data), do: data + defp check_remote_picture_path(data), do: {:ok, data} + + # Sometimes paths have "/" in front, sometimes not + defp correct_path("/" <> _ = path), do: path + defp correct_path(path), do: "/#{path}" # Twitter requires a well-know crawler user-agent to show server-rendered data defp default_user_agent("https://twitter.com/" <> _) do From c023ba63fedf25ce32c52b2e781596daecb7bb36 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 23 Apr 2021 19:12:42 +0200 Subject: [PATCH 09/31] Add org.opencontainers.image.source annotation to the Docker image Signed-off-by: Thomas Citharel --- docker/production/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile index 07136160c..4865986d7 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile @@ -42,6 +42,7 @@ LABEL org.opencontainers.image.title="mobilizon" \ org.opencontainers.image.vendor="joinmobilizon.org" \ org.opencontainers.image.documentation="https://docs.joinmobilizon.org" \ org.opencontainers.image.licenses="AGPL-3.0" \ + org.opencontainers.image.source="https://framagit.org/framasoft/mobilizon" \ org.opencontainers.image.url="https://joinmobilizon.org" \ org.opencontainers.image.revision=$VCS_REF \ org.opencontainers.image.created=$BUILD_DATE From 18279f964b17c9cf7256dfe94f9a4a2341f2dfc2 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 23 Apr 2021 19:12:52 +0200 Subject: [PATCH 10/31] Fix building Docker images on master Signed-off-by: Thomas Citharel --- .gitlab-ci.yml | 2 +- docker/production/Dockerfile | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a0dfb27c4..2b386e9a8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -184,7 +184,7 @@ pages: - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$CI_REGISTRY_AUTH\",\"email\":\"$CI_REGISTRY_EMAIL\"}}}" > /kaniko/.docker/config.json script: - - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/docker/production/Dockerfile --destination $DOCKER_IMAGE_NAME --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP --build-arg CI_COMMIT_TAG=$CI_COMMIT_TAG + - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/docker/production/Dockerfile --destination $DOCKER_IMAGE_NAME --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP build-docker-master: <<: *docker diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile index 4865986d7..b6f752afb 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile @@ -34,8 +34,6 @@ FROM alpine ARG BUILD_DATE ARG VCS_REF -ARG CI_COMMIT_TAG -ARG MOBILIZON_VERSION=${CI_COMMIT_TAG} LABEL org.opencontainers.image.title="mobilizon" \ org.opencontainers.image.description="Mobilizon for Docker" \ @@ -58,7 +56,7 @@ EXPOSE 4000 ENV MOBILIZON_DOCKER=true COPY --from=builder --chown=nobody:nobody _build/prod/rel/mobilizon ./ -RUN cp /releases/${MOBILIZON_VERSION}/runtime.exs /etc/mobilizon/config.exs +RUN cp /releases/*/runtime.exs /etc/mobilizon/config.exs COPY docker/production/docker-entrypoint.sh ./ ENTRYPOINT ["./docker-entrypoint.sh"] From fab277744cd5a0a5406728acea3dc20ec59bd499 Mon Sep 17 00:00:00 2001 From: Thomas Frenzel Date: Fri, 23 Apr 2021 18:28:31 +0000 Subject: [PATCH 11/31] Translated using Weblate (German) Currently translated at 100.0% (987 of 987 strings) Translation: Mobilizon/Frontend Translate-URL: https://weblate.framasoft.org/projects/mobilizon/frontend/de/ --- js/src/i18n/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/i18n/de.json b/js/src/i18n/de.json index db926a5d1..a8bb233ff 100644 --- a/js/src/i18n/de.json +++ b/js/src/i18n/de.json @@ -1020,7 +1020,7 @@ "Your timezone is currently set to {timezone}.": "Ihre Zeitzone ist aktuell {timezone}.", "Your timezone was detected as {timezone}.": "Ihre Zeitzone wurde erkannt als {timezone}.", "Your timezone {timezone} isn't supported.": "Ihre Zeitzone {timezone} wird nicht unterstützt.", - "Your upcoming events": "Deine bevorstehenden Veranstaltungen", + "Your upcoming events": "Ihre bevorstehenden Veranstaltungen", "[This comment has been deleted by it's author]": "[Dieser Kommentar wurde vom Autor entfernt]", "[This comment has been deleted]": "[Ihr Kommentar wurde gelöscht]", "[deleted]": "[gelöscht]", From ed52474b5172c0d581ab72e77c11380f9dd0b41c Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 26 Apr 2021 09:21:00 +0200 Subject: [PATCH 12/31] Disable sentry logging unknown activities Signed-off-by: Thomas Citharel --- lib/federation/activity_pub/transmogrifier.ex | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/federation/activity_pub/transmogrifier.ex b/lib/federation/activity_pub/transmogrifier.ex index 3399e9b9b..bf79f5ac2 100644 --- a/lib/federation/activity_pub/transmogrifier.ex +++ b/lib/federation/activity_pub/transmogrifier.ex @@ -774,10 +774,6 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do Logger.info("Handing something with type #{object["type"]} not supported") Logger.debug(inspect(object)) - Sentry.capture_message("Handing something with type #{object["type"]} not supported", - extra: %{object: object} - ) - {:error, :not_supported} end From 8185fcd0bde21f0e8cdfd45e2bcbe80b7e2daa11 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 26 Apr 2021 10:13:29 +0200 Subject: [PATCH 13/31] Refresh after invite accept only if remote group Signed-off-by: Thomas Citharel --- lib/federation/activity_pub/activity_pub.ex | 14 ++++++++++---- lib/graphql/resolvers/member.ex | 2 -- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/federation/activity_pub/activity_pub.ex b/lib/federation/activity_pub/activity_pub.ex index 67be09626..a46a02cbb 100644 --- a/lib/federation/activity_pub/activity_pub.ex +++ b/lib/federation/activity_pub/activity_pub.ex @@ -824,10 +824,7 @@ defmodule Mobilizon.Federation.ActivityPub do Mobilizon.Service.Activity.Member.insert_activity(member, subject: "member_approved" ), - _ <- - unless(is_nil(member.parent.domain), - do: Refresher.fetch_group(member.parent.url, member.actor) - ), + _ <- maybe_refresh_group(member), Absinthe.Subscription.publish(Endpoint, member.actor, group_membership_changed: [ Actor.preferred_username_and_domain(member.parent), @@ -866,6 +863,7 @@ defmodule Mobilizon.Federation.ActivityPub do Mobilizon.Service.Activity.Member.insert_activity(member, subject: "member_accepted_invitation" ), + _ <- maybe_refresh_group(member), accept_data <- %{ "type" => "Accept", "attributedTo" => member.parent.url, @@ -879,6 +877,14 @@ defmodule Mobilizon.Federation.ActivityPub do end end + defp maybe_refresh_group(%Member{ + parent: %Actor{domain: parent_domain, url: parent_url}, + actor: %Actor{} = actor + }) do + unless is_nil(parent_domain), + do: Refresher.fetch_group(parent_url, actor) + end + @spec reject_join(Participant.t(), map()) :: {:ok, Participant.t(), Activity.t()} | any() defp reject_join(%Participant{} = participant, additional) do with {:ok, %Participant{} = participant} <- diff --git a/lib/graphql/resolvers/member.ex b/lib/graphql/resolvers/member.ex index 8af57bba5..7db30908f 100644 --- a/lib/graphql/resolvers/member.ex +++ b/lib/graphql/resolvers/member.ex @@ -109,8 +109,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do member, true ) do - # Launch an async task to refresh the group profile, fetch resources, discussions, members - Refresher.fetch_group(member.parent.url, actor) {:ok, member} else {:is_same_actor, false} -> From 625ed64382bcb10bf70cfb89100c7cc41be25e63 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 26 Apr 2021 10:45:42 +0200 Subject: [PATCH 14/31] Upgrade deps Signed-off-by: Thomas Citharel --- js/yarn.lock | 12 ++++++------ mix.exs | 2 +- mix.lock | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/js/yarn.lock b/js/yarn.lock index 1f174989f..971dea394 100644 --- a/js/yarn.lock +++ b/js/yarn.lock @@ -3318,9 +3318,9 @@ bser@2.1.1: node-int64 "^0.4.0" buefy@^0.9.0: - version "0.9.6" - resolved "https://registry.yarnpkg.com/buefy/-/buefy-0.9.6.tgz#83c026c4a6f8fdcab80ded59181efc20873e3a99" - integrity sha512-qoYtbTf78xvC5fcRsuUKqUizJCAk2rg6LiAzON8X1G0GTsHkCWRWBHsJmU/jk1/6B+TQ10pSGkQgB+OLrREeXg== + version "0.9.7" + resolved "https://registry.yarnpkg.com/buefy/-/buefy-0.9.7.tgz#694e73fe0b32632a53d94c5ba9cfa4468363badd" + integrity sha512-Fli0ZjNDgtFtHm0LItWmfhNJ1oLjDwPzUWccvwXXoo2mADXaH8JQxyhY+drUuUV5/GMu5PtwqQSqPgZy942VZg== dependencies: bulma "0.9.2" @@ -4088,9 +4088,9 @@ core-js@^2.4.0, core-js@^2.5.0: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.6.4: - version "3.10.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.2.tgz#17cb038ce084522a717d873b63f2b3ee532e2cd5" - integrity sha512-W+2oVYeNghuBr3yTzZFQ5rfmjZtYB/Ubg87R5YOmlGrIb+Uw9f7qjUbhsj+/EkXhcV7eOD3jiM4+sgraX3FZUw== + version "3.11.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.11.0.tgz#05dac6aa70c0a4ad842261f8957b961d36eb8926" + integrity sha512-bd79DPpx+1Ilh9+30aT5O1sgpQd4Ttg8oqkqi51ZzhedMM1omD2e6IOF48Z/DzDCZ2svp49tN/3vneTK6ZBkXw== core-js@^3.6.5: version "3.10.1" diff --git a/mix.exs b/mix.exs index c3da82aa8..a03e18511 100644 --- a/mix.exs +++ b/mix.exs @@ -119,7 +119,7 @@ defmodule Mobilizon.Mixfile do {:http_sign, "~> 0.1.1"}, {:ecto_enum, "~> 1.4"}, {:ex_ical, "~> 0.2"}, - {:bamboo, "~> 2.1", override: true}, + {:bamboo, "~> 2.1"}, {:bamboo_phoenix, "~> 1.0"}, {:bamboo_smtp, "~> 4.0"}, {:geolix, "~> 2.0"}, diff --git a/mix.lock b/mix.lock index a8aa73c22..fe5899cbc 100644 --- a/mix.lock +++ b/mix.lock @@ -1,12 +1,12 @@ %{ "absinthe": {:hex, :absinthe, "1.6.4", "d2958908b72ce146698de8ccbc03622630471eb0e354e06823aaef183e5067bd", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e9c1cf36d86c704cb9a9c78db62d1c2676b03e0f61a28a23fc42749e8cd41ae"}, "absinthe_phoenix": {:hex, :absinthe_phoenix, "2.0.1", "112cb3468db2748a85bd8bd3f4d6d33f37408a96cb170077026ace96ddb1bab2", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:absinthe_plug, "~> 1.5", [hex: :absinthe_plug, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "e773adc876fbc84fb05a82e125d9c263f129520b36e5576554ffcb8cf49db445"}, - "absinthe_plug": {:hex, :absinthe_plug, "1.5.7", "c9954c5eff0a11be185f73987533d38d6aa44ca6bd5253de32de099f82996ce5", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2e7d766c5b3ec57aed06db0d420674bc0ff6998c55c85f5fb9bb0d3dcf7ad20d"}, + "absinthe_plug": {:hex, :absinthe_plug, "1.5.8", "38d230641ba9dca8f72f1fed2dfc8abd53b3907d1996363da32434ab6ee5d6ab", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bbb04176647b735828861e7b2705465e53e2cf54ccf5a73ddd1ebd855f996e5a"}, "argon2_elixir": {:hex, :argon2_elixir, "2.4.0", "2a22ea06e979f524c53b42b598fc6ba38cdcbc977a155e33e057732cfb1fb311", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "4ea82e183cf8e7f66dab1f767fedcfe6a195e140357ef2b0423146b72e0a551d"}, "atomex": {:hex, :atomex, "0.4.1", "7d3910ff7795db91b9af9f8d3e65af7ac69f235adf03484995fc667a36f3edc5", [:mix], [{:xml_builder, "~> 2.1", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm", "f3ac737f7493d42cfddf917f3ac49d60e0a0cf1a35c0712851b07fe8c0a05c7a"}, "bamboo": {:hex, :bamboo, "2.1.0", "3c58f862efd74fa8c8d48a410ac592b41f7d24785e828566f7a0af549269ddc3", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f0ad2623b9a1d2dc06dcf289b59df9ebc522f49f3a21971ec87a8fce04e6d33e"}, "bamboo_phoenix": {:hex, :bamboo_phoenix, "1.0.0", "f3cc591ffb163ed0bf935d256f1f4645cd870cf436545601215745fb9cc9953f", [:mix], [{:bamboo, ">= 2.0.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.3.0", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "6db88fbb26019c84a47994bb2bd879c0887c29ce6c559bc6385fd54eb8b37dee"}, - "bamboo_smtp": {:hex, :bamboo_smtp, "4.0.0", "0cc7df161d5d440d280a6d2eb20bf80bc45ea77161728a229e5ab339dcd087cd", [:mix], [{:bamboo, "~> 2.0.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 1.1.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm", "2412015092121b9f24f3f2e654bcd98e5c5f9afb323a94f8defa22e70ba8f23d"}, + "bamboo_smtp": {:hex, :bamboo_smtp, "4.0.1", "7e48188663f6164a81183688bb263be4c3952648fcd3ce52164f44d68777f9cd", [:mix], [{:bamboo, "~> 2.1.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 1.1.1", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm", "7ff1d62ae39bfb1c14f6d3cddba0fa1482a45c2a2b497a2da601eff7099605c8"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, "cachex": {:hex, :cachex, "3.3.0", "6f2ebb8f27491fe39121bd207c78badc499214d76c695658b19d6079beeca5c2", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "d90e5ee1dde14cef33f6b187af4335b88748b72b30c038969176cd4e6ccc31a1"}, "certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"}, @@ -81,7 +81,7 @@ "jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"}, "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"}, "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, - "junit_formatter": {:hex, :junit_formatter, "3.1.0", "3f69c61c5413750f9c45e367d77aabbeac9b395acf478d8e70b4ee9d1989c709", [:mix], [], "hexpm", "da52401a93f711fc4f77ffabdda68f9a16fcad5d96f5fce4ae606ab1d73b72f4"}, + "junit_formatter": {:hex, :junit_formatter, "3.2.0", "6a64d6327c48472e983af7645aeb1d756cad4b33c5ab3a5db820f13d6d2b85fe", [:mix], [], "hexpm", "043d6f660667ee64c6d7e5edb867b7900c395356346e77185879449b50486681"}, "linkify": {:hex, :linkify, "0.5.0", "e0ea8de73ff44742d6a889721221f4c4eccaad5284957ee9832ffeb347602d54", [:mix], [], "hexpm", "4ccd958350aee7c51c89e21f05b15d30596ebbba707e051d21766be1809df2d7"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, @@ -111,7 +111,7 @@ "plug_cowboy": {:hex, :plug_cowboy, "2.5.0", "51c998f788c4e68fc9f947a5eba8c215fbb1d63a520f7604134cab0270ea6513", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5b2c8925a5e2587446f33810a58c01e66b3c345652eeec809b76ba007acde71a"}, "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, - "postgrex": {:hex, :postgrex, "0.15.8", "f5e782bbe5e8fa178d5e3cd1999c857dc48eda95f0a4d7f7bd92a50e84a0d491", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "698fbfacea34c4cf22c8281abeb5cf68d99628d541874f085520ab3b53d356fe"}, + "postgrex": {:hex, :postgrex, "0.15.9", "46f8fe6f25711aeb861c4d0ae09780facfdf3adbd2fb5594ead61504dd489bda", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "610719103e4cb2223d4ab78f9f0f3e720320eeca6011415ab4137ddef730adee"}, "progress_bar": {:hex, :progress_bar, "2.0.1", "7b40200112ae533d5adceb80ff75fbe66dc753bca5f6c55c073bfc122d71896d", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "2519eb58a2f149a3a094e729378256d8cb6d96a259ec94841bd69fdc71f18f87"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, "remote_ip": {:hex, :remote_ip, "1.0.0", "3d7fb45204a5704443f480cee9515e464997f52c35e0a60b6ece1f81484067ae", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9e9fcad4e50c43b5234bb6a9629ed6ab223f3ed07147bd35470e4ee5c8caf907"}, From fab8ee6f05cf48fbf93f7b189e08d24bf9991979 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 26 Apr 2021 17:27:27 +0200 Subject: [PATCH 15/31] Fix organizer metadata overflow Signed-off-by: Thomas Citharel --- .../components/Event/EventMetadataBlock.vue | 1 + js/src/views/Event/Event.vue | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/js/src/components/Event/EventMetadataBlock.vue b/js/src/components/Event/EventMetadataBlock.vue index 4aab57660..5b20bbc01 100644 --- a/js/src/components/Event/EventMetadataBlock.vue +++ b/js/src/components/Event/EventMetadataBlock.vue @@ -33,6 +33,7 @@ div.eventMetadataBlock { p { flex: 1; + overflow: hidden; &.padding-left { padding-left: 20px; diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue index 779de1b68..5db7f2379 100755 --- a/js/src/views/Event/Event.vue +++ b/js/src/views/Event/Event.vue @@ -341,7 +341,10 @@ :endsOn="event.endsOn" /> - +