diff --git a/changelog.d/hashtag-feeds-restricted.add b/changelog.d/hashtag-feeds-restricted.add new file mode 100644 index 0000000000..accac9c9cc --- /dev/null +++ b/changelog.d/hashtag-feeds-restricted.add @@ -0,0 +1 @@ +Repesct :restrict_unauthenticated for hashtag rss/atom feeds \ No newline at end of file diff --git a/changelog.d/incoming-blocks.fix b/changelog.d/incoming-blocks.fix new file mode 100644 index 0000000000..3228d7318c --- /dev/null +++ b/changelog.d/incoming-blocks.fix @@ -0,0 +1 @@ +Fix incoming Block activities being rejected diff --git a/changelog.d/ldap-password-change.add b/changelog.d/ldap-password-change.add new file mode 100644 index 0000000000..7ca555ee47 --- /dev/null +++ b/changelog.d/ldap-password-change.add @@ -0,0 +1 @@ +LDAP now supports users changing their passwords diff --git a/changelog.d/remote-report-policy.add b/changelog.d/remote-report-policy.add new file mode 100644 index 0000000000..1cf25b1a8f --- /dev/null +++ b/changelog.d/remote-report-policy.add @@ -0,0 +1 @@ +Added RemoteReportPolicy from Rebased for handling bogus federated reports diff --git a/installation/openldap/pw_self_service.ldif b/installation/openldap/pw_self_service.ldif new file mode 100644 index 0000000000..463dabbfb5 --- /dev/null +++ b/installation/openldap/pw_self_service.ldif @@ -0,0 +1,7 @@ +dn: olcDatabase={1}mdb,cn=config +changetype: modify +add: olcAccess +olcAccess: {1}to attrs=userPassword + by self write + by anonymous auth + by * none diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index 5d8d8f1c68..e341ab8df9 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -102,6 +102,7 @@ defmodule Pleroma.Constants do const(activity_types, do: [ + "Block", "Create", "Update", "Delete", @@ -130,6 +131,10 @@ defmodule Pleroma.Constants do ] ) + const(object_types, + do: ~w[Event Question Answer Audio Video Image Article Note Page ChatMessage] + ) + # basic regex, just there to weed out potential mistakes # https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 const(mime_regex, diff --git a/lib/pleroma/ldap.ex b/lib/pleroma/ldap.ex index 46a2d0c17b..b591c2918a 100644 --- a/lib/pleroma/ldap.ex +++ b/lib/pleroma/ldap.ex @@ -15,6 +15,14 @@ def start_link(_) do GenServer.start_link(__MODULE__, [], name: __MODULE__) end + def bind_user(name, password) do + GenServer.call(__MODULE__, {:bind_user, name, password}) + end + + def change_password(name, password, new_password) do + GenServer.call(__MODULE__, {:change_password, name, password, new_password}) + end + @impl true def init(state) do case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do @@ -47,13 +55,42 @@ def handle_continue(:connect, _state), do: do_handle_connect() def handle_info(:connect, _state), do: do_handle_connect() def handle_info({:bind_after_reconnect, name, password, from}, state) do - result = bind_user(state[:handle], name, password) + result = do_bind_user(state[:handle], name, password) GenServer.reply(from, result) {:noreply, state} end + @impl true + def handle_call({:bind_user, name, password}, from, state) do + case do_bind_user(state[:handle], name, password) do + :needs_reconnect -> + Process.send(self(), {:bind_after_reconnect, name, password, from}, []) + {:noreply, state, {:continue, :connect}} + + result -> + {:reply, result, state, :hibernate} + end + end + + def handle_call({:change_password, name, password, new_password}, _from, state) do + result = change_password(state[:handle], name, password, new_password) + + {:reply, result, state, :hibernate} + end + + @impl true + def terminate(_, state) do + handle = Keyword.get(state, :handle) + + if not is_nil(handle) do + :eldap.close(handle) + end + + :ok + end + defp do_handle_connect do state = case connect() do @@ -71,33 +108,6 @@ defp do_handle_connect do {:noreply, state} end - @impl true - def handle_call({:bind_user, name, password}, from, state) do - case bind_user(state[:handle], name, password) do - :needs_reconnect -> - Process.send(self(), {:bind_after_reconnect, name, password, from}, []) - {:noreply, state, {:continue, :connect}} - - result -> - {:reply, result, state, :hibernate} - end - end - - @impl true - def terminate(_, state) do - handle = Keyword.get(state, :handle) - - if not is_nil(handle) do - :eldap.close(handle) - end - - :ok - end - - def bind_user(name, password) do - GenServer.call(__MODULE__, {:bind_user, name, password}) - end - defp connect do ldap = Config.get(:ldap, []) host = Keyword.get(ldap, :host, "localhost") @@ -161,18 +171,17 @@ defp connect do end end - defp bind_user(handle, name, password) do - uid = Config.get([:ldap, :uid], "cn") - base = Config.get([:ldap, :base]) + defp do_bind_user(handle, name, password) do + dn = make_dn(name) - case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do + case :eldap.simple_bind(handle, dn, password) do :ok -> case fetch_user(name) do %User{} = user -> user _ -> - register_user(handle, base, uid, name) + register_user(handle, ldap_base(), ldap_uid(), name) end # eldap does not inform us of socket closure @@ -231,6 +240,14 @@ defp try_register(name, attributes) do end end + defp change_password(handle, name, password, new_password) do + dn = make_dn(name) + + with :ok <- :eldap.simple_bind(handle, dn, password) do + :eldap.modify_password(handle, dn, to_charlist(new_password), to_charlist(password)) + end + end + defp decode_certfile(file) do with {:ok, data} <- File.read(file) do data @@ -242,4 +259,13 @@ defp decode_certfile(file) do [] end end + + defp ldap_uid, do: to_charlist(Config.get([:ldap, :uid], "cn")) + defp ldap_base, do: to_charlist(Config.get([:ldap, :base])) + + defp make_dn(name) do + uid = ldap_uid() + base = ldap_base() + ~c"#{uid}=#{name},#{base}" + end end diff --git a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex index 0bd83d8f00..fa0610bf10 100644 --- a/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex @@ -9,6 +9,7 @@ def filter(%{"type" => "Flag"} = object) do with {_, false} <- {:local, local?(object)}, {:ok, _} <- maybe_reject_all(object), {:ok, _} <- maybe_reject_anonymous(object), + {:ok, _} <- maybe_reject_third_party(object), {:ok, _} <- maybe_reject_empty_message(object) do {:ok, object} else @@ -37,6 +38,22 @@ defp maybe_reject_anonymous(%{"actor" => actor} = object) do end end + defp maybe_reject_third_party(%{"object" => objects} = object) do + {_, to} = + case objects do + [head | tail] when is_binary(head) -> {tail, head} + s when is_binary(s) -> {[], s} + _ -> {[], ""} + end + + with true <- Config.get([:mrf_remote_report, :reject_third_party]), + false <- String.starts_with?(to, Pleroma.Web.Endpoint.url()) do + {:reject, "[RemoteReportPolicy] Third-party: #{to}"} + else + _ -> {:ok, object} + end + end + defp maybe_reject_empty_message(%{"content" => content} = object) when is_binary(content) and content != "" do {:ok, object} @@ -83,6 +100,12 @@ def config_description do description: "Reject anonymous remote reports?", suggestions: [true] }, + %{ + key: :reject_third_party, + type: :boolean, + description: "Reject reports on users from third-party instances?", + suggestions: [true] + }, %{ key: :reject_empty_message, type: :boolean, diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index f44fbfee7a..ebe4de942e 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do @behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating + import Pleroma.Constants, only: [activity_types: 0, object_types: 0] + alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object @@ -42,6 +44,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do @impl true def validate(object, meta) + # This overload works together with the InboxGuardPlug + # and ensures that we are not accepting any activity type + # that cannot pass InboxGuardPlug. + # If we want to support any more activity types, make sure to + # add it in Pleroma.Constants's activity_types or object_types, + # and, if applicable, allowed_activity_types_from_strangers. + def validate(%{"type" => type}, _meta) + when type not in activity_types() and type not in object_types(), + do: {:error, :not_allowed_object_type} + def validate(%{"type" => "Block"} = block_activity, meta) do with {:ok, block_activity} <- block_activity diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex index 01bf1575c4..95be892cd6 100644 --- a/lib/pleroma/web/auth/authenticator.ex +++ b/lib/pleroma/web/auth/authenticator.ex @@ -10,4 +10,9 @@ defmodule Pleroma.Web.Auth.Authenticator do @callback handle_error(Plug.Conn.t(), any()) :: any() @callback auth_template() :: String.t() | nil @callback oauth_consumer_template() :: String.t() | nil + + @callback change_password(Pleroma.User.t(), String.t(), String.t(), String.t()) :: + {:ok, Pleroma.User.t()} | {:error, term()} + + @optional_callbacks change_password: 4 end diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 7eb06183d3..ec6601fb90 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -30,4 +30,13 @@ def get_user(%Plug.Conn{} = conn) do error end end + + def change_password(user, password, new_password, new_password) do + case LDAP.change_password(user.nickname, password, new_password) do + :ok -> {:ok, user} + e -> e + end + end + + def change_password(_, _, _, _), do: {:error, :password_confirmation} end diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index 09a58eb66b..0da3f19fce 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do alias Pleroma.Registration alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.AuthenticationPlug import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1] @@ -101,4 +102,23 @@ def handle_error(%Plug.Conn{} = _conn, error) do def auth_template, do: nil def oauth_consumer_template, do: nil + + @doc "Changes Pleroma.User password in the database" + def change_password(user, password, new_password, new_password) do + case CommonAPI.Utils.confirm_current_password(user, password) do + {:ok, user} -> + with {:ok, _user} <- + User.reset_password(user, %{ + password: new_password, + password_confirmation: new_password + }) do + {:ok, user} + end + + error -> + error + end + end + + def change_password(_, _, _, _), do: {:error, :password_confirmation} end diff --git a/lib/pleroma/web/auth/wrapper_authenticator.ex b/lib/pleroma/web/auth/wrapper_authenticator.ex index a077cfa416..97b901036c 100644 --- a/lib/pleroma/web/auth/wrapper_authenticator.ex +++ b/lib/pleroma/web/auth/wrapper_authenticator.ex @@ -39,4 +39,8 @@ def oauth_consumer_template do implementation().oauth_consumer_template() || Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html") end + + @impl true + def change_password(user, password, new_password, new_password_confirmation), + do: implementation().change_password(user, password, new_password, new_password_confirmation) end diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex index e60767327c..02d6392960 100644 --- a/lib/pleroma/web/feed/tag_controller.ex +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.Feed.TagController do alias Pleroma.Web.Feed.FeedView def feed(conn, params) do - if Config.get!([:instance, :public]) do + if not Config.restrict_unauthenticated_access?(:timelines, :local) do render_feed(conn, params) else render_error(conn, :not_found, "Not found") @@ -18,10 +18,12 @@ def feed(conn, params) do end defp render_feed(conn, %{"tag" => raw_tag} = params) do + local_only = Config.restrict_unauthenticated_access?(:timelines, :federated) + {format, tag} = parse_tag(raw_tag) activities = - %{type: ["Create"], tag: tag} + %{type: ["Create"], tag: tag, local_only: local_only} |> Pleroma.Maps.put_if_present(:max_id, params["max_id"]) |> ActivityPub.fetch_public_activities() diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index e71b926a06..a41eb5a317 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Healthcheck alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator alias Pleroma.Web.CommonAPI alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger @@ -195,19 +196,21 @@ def change_password( %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn, _ ) do - case CommonAPI.Utils.confirm_current_password(user, body_params.password) do - {:ok, user} -> - with {:ok, _user} <- - User.reset_password(user, %{ - password: body_params.new_password, - password_confirmation: body_params.new_password_confirmation - }) do - json(conn, %{status: "success"}) - else - {:error, changeset} -> - {_, {error, _}} = Enum.at(changeset.errors, 0) - json(conn, %{error: "New password #{error}."}) - end + with {:ok, %User{}} <- + Authenticator.change_password( + user, + body_params.password, + body_params.new_password, + body_params.new_password_confirmation + ) do + json(conn, %{status: "success"}) + else + {:error, %Ecto.Changeset{} = changeset} -> + {_, {error, _}} = Enum.at(changeset.errors, 0) + json(conn, %{error: "New password #{error}."}) + + {:error, :password_confirmation} -> + json(conn, %{error: "New password does not match confirmation."}) {:error, msg} -> json(conn, %{error: msg}) diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex index 7d69bea548..a9afe9d63a 100644 --- a/lib/pleroma/workers/poll_worker.ex +++ b/lib/pleroma/workers/poll_worker.ex @@ -22,9 +22,11 @@ defmodule Pleroma.Workers.PollWorker do def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)}, {:ok, notifications} <- Notification.create_poll_notifications(activity) do - # Schedule a final refresh - __MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id}) - |> Oban.insert() + unless activity.local do + # Schedule a final refresh + __MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id}) + |> Oban.insert() + end Notification.stream(notifications) else diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 6464cbc387..96d5fa06bc 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -1338,6 +1338,27 @@ test "forwarded report from mastodon", %{conn: conn} do html_body: ~r/#{note.data["object"]}/i ) end + + test "it accepts an incoming Block", %{conn: conn, data: data} do + user = insert(:user) + + data = + data + |> Map.put("type", "Block") + |> Map.put("to", [user.ap_id]) + |> Map.put("cc", []) + |> Map.put("object", user.ap_id) + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end end describe "GET /users/:nickname/outbox" do diff --git a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs index 43258a7f60..8d2a6b4fa9 100644 --- a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs @@ -13,7 +13,8 @@ test "doesn't impact local report" do activity = %{ "type" => "Flag", - "actor" => "http://localhost:4001/actor" + "actor" => "http://localhost:4001/actor", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -25,7 +26,8 @@ test "rejects anonymous report if `reject_anonymous: true`" do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/actor" + "actor" => "https://mastodon.social/actor", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:reject, _} = RemoteReportPolicy.filter(activity) @@ -37,7 +39,47 @@ test "preserves anonymous report if `reject_anonymous: false`" do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/actor" + "actor" => "https://mastodon.social/actor", + "object" => ["https://mastodon.online/users/Gargron"] + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end + + test "rejects report on third party if `reject_third_party: true`" do + clear_config([:mrf_remote_report, :reject_third_party], true) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] + } + + assert {:reject, _} = RemoteReportPolicy.filter(activity) + end + + test "preserves report on first party if `reject_third_party: true`" do + clear_config([:mrf_remote_report, :reject_third_party], true) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["http://localhost:4001/actor"] + } + + assert {:ok, _} = RemoteReportPolicy.filter(activity) + end + + test "preserves report on third party if `reject_third_party: false`" do + clear_config([:mrf_remote_report, :reject_third_party], false) + clear_config([:mrf_remote_report, :reject_empty_message], false) + + activity = %{ + "type" => "Flag", + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -49,7 +91,8 @@ test "rejects empty message report if `reject_empty_message: true`" do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/users/Gargron" + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:reject, _} = RemoteReportPolicy.filter(activity) @@ -62,6 +105,7 @@ test "rejects empty message report (\"\") if `reject_empty_message: true`" do activity = %{ "type" => "Flag", "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"], "content" => "" } @@ -74,7 +118,8 @@ test "preserves empty message report if `reject_empty_message: false`" do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/users/Gargron" + "actor" => "https://mastodon.social/users/Gargron", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -86,7 +131,8 @@ test "preserves anonymous, empty message report with all settings disabled" do activity = %{ "type" => "Flag", - "actor" => "https://mastodon.social/actor" + "actor" => "https://mastodon.social/actor", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:ok, _} = RemoteReportPolicy.filter(activity) @@ -100,7 +146,8 @@ test "reject remote report if `reject_all: true`" do activity = %{ "type" => "Flag", "actor" => "https://mastodon.social/users/Gargron", - "content" => "Transphobia" + "content" => "Transphobia", + "object" => ["https://mastodon.online/users/Gargron"] } assert {:reject, _} = RemoteReportPolicy.filter(activity) diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index 7d196b228e..662235f31f 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -191,4 +191,60 @@ test "returns 404 for tags feed", %{conn: conn} do |> response(404) end end + + describe "restricted for unauthenticated" do + test "returns 404 when local timeline is disabled", %{conn: conn} do + clear_config([:restrict_unauthenticated, :timelines], %{local: true, federated: false}) + + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) + |> response(404) + end + + test "returns local posts only when federated timeline is disabled", %{conn: conn} do + clear_config([:restrict_unauthenticated, :timelines], %{local: false, federated: true}) + + local_user = insert(:user) + remote_user = insert(:user, local: false) + + local_note = + insert(:note, + user: local_user, + data: %{ + "content" => "local post #PleromaArt", + "summary" => "", + "tag" => ["pleromaart"] + } + ) + + remote_note = + insert(:note, + user: remote_user, + data: %{ + "content" => "remote post #PleromaArt", + "summary" => "", + "tag" => ["pleromaart"] + }, + local: false + ) + + insert(:note_activity, user: local_user, note: local_note) + insert(:note_activity, user: remote_user, note: remote_note, local: false) + + response = + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) + |> response(200) + + xml = parse(response) + + assert xpath(xml, ~x"//channel/title/text()") == ~c"#pleromaart" + + assert xpath(xml, ~x"//channel/item/title/text()"l) == [ + ~c"local post #PleromaArt" + ] + end + end end diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs index 70eb7c4226..a7cbbdb83c 100644 --- a/test/pleroma/workers/poll_worker_test.exs +++ b/test/pleroma/workers/poll_worker_test.exs @@ -11,10 +11,10 @@ defmodule Pleroma.Workers.PollWorkerTest do alias Pleroma.Workers.PollWorker - test "poll notification job" do + test "local poll ending notification job" do user = insert(:user) question = insert(:question, user: user) - activity = insert(:question_activity, question: question) + activity = insert(:question_activity, question: question, user: user) PollWorker.schedule_poll_end(activity) @@ -45,14 +45,38 @@ test "poll notification job" do assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], :_)) assert called(Pleroma.Web.Push.send(:_)) - # Ensure we scheduled a final refresh of the poll - assert_enqueued( + # Skip refreshing polls for local activities + assert activity.local + + refute_enqueued( worker: PollWorker, args: %{"op" => "refresh", "activity_id" => activity.id} ) end end + test "remote poll ending notification job schedules refresh" do + user = insert(:user, local: false) + question = insert(:question, user: user) + activity = insert(:question_activity, question: question, user: user) + + PollWorker.schedule_poll_end(activity) + + expected_job_args = %{"activity_id" => activity.id, "op" => "poll_end"} + + assert_enqueued(args: expected_job_args) + + [job] = all_enqueued(worker: PollWorker) + PollWorker.perform(job) + + refute activity.local + + assert_enqueued( + worker: PollWorker, + args: %{"op" => "refresh", "activity_id" => activity.id} + ) + end + test "poll refresh" do user = insert(:user, local: false) question = insert(:question, user: user) diff --git a/test/support/factory.ex b/test/support/factory.ex index ce94955cab..d56fc9fcea 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -510,7 +510,8 @@ def question_activity_factory(attrs \\ %{}) do %Pleroma.Activity{ data: data, actor: data["actor"], - recipients: data["to"] + recipients: data["to"], + local: user.local } |> Map.merge(attrs) end