commit: 2baa9b007221a654dbfe2aad9353e5815f9d1ddb
parent 23e5eed4e0e61ea65bd895bee7d8a137fccf3307
Author: Lain Soykaf <lain@lain.com>
Date: Tue, 12 Nov 2024 14:33:30 +0400
Merge branch 'develop' into pleroma-token-view-scopes
Diffstat:
69 files changed, 827 insertions(+), 277 deletions(-)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
@@ -1,8 +1,8 @@
-image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25
+image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25
variables: &global_variables
# Only used for the release
- ELIXIR_VER: 1.13.4
+ ELIXIR_VER: 1.14.5
POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -71,7 +71,7 @@ check-changelog:
tags:
- amd64
-build-1.13.4-otp-25:
+build-1.14.5-otp-25:
extends:
- .build_changes_policy
- .using-ci-base
@@ -119,7 +119,7 @@ benchmark:
- mix ecto.migrate
- mix pleroma.load_testing
-unit-testing-1.13.4-otp-25:
+unit-testing-1.14.5-otp-25:
extends:
- .build_changes_policy
- .using-ci-base
diff --git a/Dockerfile b/Dockerfile
@@ -1,7 +1,8 @@
+# https://hub.docker.com/r/hexpm/elixir/tags
ARG ELIXIR_IMG=hexpm/elixir
-ARG ELIXIR_VER=1.13.4
-ARG ERLANG_VER=24.3.4.15
-ARG ALPINE_VER=3.17.5
+ARG ELIXIR_VER=1.14.5
+ARG ERLANG_VER=25.3.2.14
+ARG ALPINE_VER=3.17.9
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build
diff --git a/changelog.d/debian-install-improve.skip b/changelog.d/debian-install-improve.skip
@@ -0,0 +1 @@
+Fixed a formatting issue that had a required commend embedded in a textblock, and change the language to make it a bit more idiomatic.
+\ No newline at end of file
diff --git a/changelog.d/docs-vips.skip b/changelog.d/docs-vips.skip
diff --git a/changelog.d/elixir-1.14-docker.skip b/changelog.d/elixir-1.14-docker.skip
diff --git a/changelog.d/elixir.change b/changelog.d/elixir.change
@@ -0,0 +1 @@
+Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release
diff --git a/changelog.d/freebsd-docs.skip b/changelog.d/freebsd-docs.skip
diff --git a/changelog.d/hashtag-feeds-restricted.add 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
@@ -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
@@ -0,0 +1 @@
+LDAP now supports users changing their passwords
diff --git a/changelog.d/mediav2_status.fix b/changelog.d/mediav2_status.fix
@@ -0,0 +1 @@
+Fix /api/v2/media returning the wrong status code (202) for media processed synchronously
diff --git a/changelog.d/mrf-id_filter.add b/changelog.d/mrf-id_filter.add
@@ -0,0 +1 @@
+Add `id_filter` to MRF to filter URLs and their domain prior to fetching
+\ No newline at end of file
diff --git a/changelog.d/oban-update.change b/changelog.d/oban-update.change
@@ -0,0 +1 @@
+Oban updated to 2.18.3
diff --git a/changelog.d/poll-refresh.change b/changelog.d/poll-refresh.change
@@ -0,0 +1 @@
+Poll results refreshing is handled asynchronously and will not attempt to keep fetching updates to a closed poll.
diff --git a/changelog.d/profile-image-descriptions.skip b/changelog.d/profile-image-descriptions.skip
diff --git a/changelog.d/release-tuning.change b/changelog.d/release-tuning.change
@@ -0,0 +1 @@
+Tuning for release builds to lower CPU usage.
diff --git a/changelog.d/remote-report-policy.add b/changelog.d/remote-report-policy.add
@@ -0,0 +1 @@
+Added RemoteReportPolicy from Rebased for handling bogus federated reports
diff --git a/ci/elixir-1.12/Dockerfile b/ci/elixir-1.12/Dockerfile
@@ -1,8 +0,0 @@
-FROM elixir:1.12.3
-
-# Single RUN statement, otherwise intermediate images are created
-# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
-RUN apt-get update &&\
- apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
- mix local.hex --force &&\
- mix local.rebar --force
diff --git a/ci/elixir-1.12/build_and_push.sh b/ci/elixir-1.12/build_and_push.sh
@@ -1 +0,0 @@
-docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.12 --push .
diff --git a/ci/elixir-1.13.4-otp-25/Dockerfile b/ci/elixir-1.13.4-otp-25/Dockerfile
@@ -1,8 +0,0 @@
-FROM elixir:1.13.4-otp-25
-
-# Single RUN statement, otherwise intermediate images are created
-# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
-RUN apt-get update &&\
- apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
- mix local.hex --force &&\
- mix local.rebar --force
diff --git a/ci/elixir-1.13.4-otp-25/build_and_push.sh b/ci/elixir-1.13.4-otp-25/build_and_push.sh
@@ -1 +0,0 @@
-docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25 --push .
diff --git a/ci/elixir-1.14.5-otp-25/Dockerfile b/ci/elixir-1.14.5-otp-25/Dockerfile
@@ -0,0 +1,8 @@
+FROM elixir:1.14.5-otp-25
+
+# Single RUN statement, otherwise intermediate images are created
+# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
+RUN apt-get update &&\
+ apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
+ mix local.hex --force &&\
+ mix local.rebar --force
diff --git a/ci/elixir-1.14.5-otp-25/build_and_push.sh b/ci/elixir-1.14.5-otp-25/build_and_push.sh
@@ -0,0 +1 @@
+docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 --push .
diff --git a/config/config.exs b/config/config.exs
@@ -434,6 +434,11 @@ config :pleroma, :mrf_follow_bot, follower_nickname: nil
config :pleroma, :mrf_inline_quote, template: "<bdi>RT:</bdi> {url}"
+config :pleroma, :mrf_remote_report,
+ reject_all: false,
+ reject_anonymous: true,
+ reject_empty_message: true
+
config :pleroma, :mrf_force_mention,
mention_parent: true,
mention_quoted: true
diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md
@@ -69,12 +69,18 @@ cd /opt/pleroma
sudo -Hu pleroma mix deps.get
```
-* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
+* Generate the configuration:
+
+```shell
+sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
+```
+
+* During this process:
* Answer with `yes` if it asks you to install `rebar3`.
* This may take some time, because parts of pleroma get compiled first.
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
-* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
+* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for production instances, `dev.secret.exs` for development instances):
```shell
sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}
diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md
@@ -14,7 +14,7 @@ Note: This article is potentially outdated because at this time we may not have
- PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
- `postgresql-contrib` 11.0以上 (同上)
-- Elixir 1.13 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
+- Elixir 1.14 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
- `erlang-dev`
- `erlang-nox`
- `git`
diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md
@@ -31,7 +31,7 @@ Setup the required services to automatically start at boot, using `sysrc(8)`.
### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
```shell
-# pkg install imagemagick ffmpeg p5-Image-ExifTool
+# pkg install imagemagick ffmpeg p5-Image-ExifTool vips
```
## Configuring Pleroma
diff --git a/docs/installation/generic_dependencies.include b/docs/installation/generic_dependencies.include
@@ -1,8 +1,8 @@
## Required dependencies
* PostgreSQL >=11.0
-* Elixir >=1.13.0 <1.17
-* Erlang OTP >=22.2.0 (supported: <27)
+* Elixir >=1.14.0 <1.17
+* Erlang OTP >=23.0.0 (supported: <27)
* git
* file / libmagic
* gcc or clang
diff --git a/docs/installation/openbsd_en.md b/docs/installation/openbsd_en.md
@@ -12,7 +12,7 @@ For any additional information regarding commands and configuration files mentio
To install them, run the following command (with doas or as root):
```
-pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg ImageMagick
+pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg ImageMagick libvips
```
Pleroma requires a reverse proxy, OpenBSD has relayd in base (and is used in this guide) and packages/ports are available for nginx (www/nginx) and apache (www/apache-httpd). Independently of the reverse proxy, [acme-client(1)](https://man.openbsd.org/acme-client) can be used to get a certificate from Let's Encrypt.
diff --git a/docs/installation/openbsd_fi.md b/docs/installation/openbsd_fi.md
@@ -18,7 +18,7 @@ Matrix-kanava #pleroma:libera.chat ovat hyviä paikkoja löytää apua
Asenna tarvittava ohjelmisto:
-`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3 cmake ffmpeg ImageMagick`
+`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3 cmake ffmpeg ImageMagick libvips`
#### Optional software
diff --git a/installation/openldap/pw_self_service.ldif 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
@@ -87,6 +87,7 @@ defmodule Pleroma.Constants do
const(activity_types,
do: [
+ "Block",
"Create",
"Update",
"Delete",
@@ -115,6 +116,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
@@ -15,6 +15,14 @@ defmodule Pleroma.LDAP 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,33 +55,16 @@ defmodule Pleroma.LDAP do
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
- defp do_handle_connect do
- state =
- case connect() do
- {:ok, handle} ->
- :eldap.controlling_process(handle, self())
- Process.link(handle)
- [handle: handle]
-
- _ ->
- Logger.error("Failed to connect to LDAP. Retrying in 5000ms")
- Process.send_after(self(), :connect, 5_000)
- []
- end
-
- {:noreply, state}
- end
-
@impl true
def handle_call({:bind_user, name, password}, from, state) do
- case bind_user(state[:handle], name, password) 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}}
@@ -83,6 +74,12 @@ defmodule Pleroma.LDAP do
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)
@@ -94,8 +91,21 @@ defmodule Pleroma.LDAP do
:ok
end
- def bind_user(name, password) do
- GenServer.call(__MODULE__, {:bind_user, name, password})
+ defp do_handle_connect do
+ state =
+ case connect() do
+ {:ok, handle} ->
+ :eldap.controlling_process(handle, self())
+ Process.link(handle)
+ [handle: handle]
+
+ _ ->
+ Logger.error("Failed to connect to LDAP. Retrying in 5000ms")
+ Process.send_after(self(), :connect, 5_000)
+ []
+ end
+
+ {:noreply, state}
end
defp connect do
@@ -161,18 +171,17 @@ defmodule Pleroma.LDAP 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 @@ defmodule Pleroma.LDAP 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 @@ defmodule Pleroma.LDAP 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/object.ex b/lib/pleroma/object.ex
@@ -99,27 +99,6 @@ defmodule Pleroma.Object do
def get_by_id(nil), do: nil
def get_by_id(id), do: Repo.get(Object, id)
- @spec get_by_id_and_maybe_refetch(integer(), list()) :: Object.t() | nil
- def get_by_id_and_maybe_refetch(id, opts \\ []) do
- with %Object{updated_at: updated_at} = object <- get_by_id(id) do
- if opts[:interval] &&
- NaiveDateTime.diff(NaiveDateTime.utc_now(), updated_at) > opts[:interval] do
- case Fetcher.refetch_object(object) do
- {:ok, %Object{} = object} ->
- object
-
- e ->
- Logger.error("Couldn't refresh #{object.data["id"]}:\n#{inspect(e)}")
- object
- end
- else
- object
- end
- else
- nil -> nil
- end
- end
-
def get_by_ap_id(nil), do: nil
def get_by_ap_id(ap_id) do
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
@@ -145,6 +145,7 @@ defmodule Pleroma.Object.Fetcher do
Logger.debug("Fetching object #{id} via AP")
with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")},
+ {_, true} <- {:mrf, MRF.id_filter(id)},
{:ok, body} <- get_object(id),
{:ok, data} <- safe_json_decode(body),
:ok <- Containment.contain_origin_from_id(id, data) do
@@ -160,6 +161,9 @@ defmodule Pleroma.Object.Fetcher do
{:error, e} ->
{:error, e}
+ {:mrf, false} ->
+ {:error, {:reject, "Filtered by id"}}
+
e ->
{:error, e}
end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
@@ -419,6 +419,11 @@ defmodule Pleroma.User do
end
end
+ def image_description(image, default \\ "")
+
+ def image_description(%{"name" => name}, _default), do: name
+ def image_description(_, default), do: default
+
# Should probably be renamed or removed
@spec ap_id(User.t()) :: String.t()
def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1542,16 +1542,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp get_actor_url(_url), do: nil
- defp normalize_image(%{"url" => url}) do
+ defp normalize_image(%{"url" => url} = data) do
%{
"type" => "Image",
"url" => [%{"href" => url}]
}
+ |> maybe_put_description(data)
end
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
defp normalize_image(_), do: nil
+ defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
+ Map.put(map, "name", description)
+ end
+
+ defp maybe_put_description(map, _), do: map
+
defp object_to_user_data(data, additional) do
fields =
data
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
@@ -108,6 +108,14 @@ defmodule Pleroma.Web.ActivityPub.MRF do
def filter(%{} = object), do: get_policies() |> filter(object)
+ def id_filter(policies, id) when is_binary(id) do
+ policies
+ |> Enum.filter(&function_exported?(&1, :id_filter, 1))
+ |> Enum.all?(& &1.id_filter(id))
+ end
+
+ def id_filter(id) when is_binary(id), do: get_policies() |> id_filter(id)
+
@impl true
def pipeline_filter(%{} = message, meta) do
object = meta[:object_data]
diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
@@ -14,5 +14,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
end
@impl true
+ def id_filter(id) do
+ Logger.debug("REJECTING #{id}")
+ false
+ end
+
+ @impl true
def describe, do: {:ok, %{}}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/policy.ex b/lib/pleroma/web/activity_pub/mrf/policy.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.Policy do
@callback filter(Pleroma.Activity.t()) :: {:ok | :reject, Pleroma.Activity.t()}
+ @callback id_filter(String.t()) :: boolean()
@callback describe() :: {:ok | :error, map()}
@callback config_description() :: %{
optional(:children) => [map()],
@@ -13,5 +14,5 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do
description: String.t()
}
@callback history_awareness() :: :auto | :manual
- @optional_callbacks config_description: 0, history_awareness: 0
+ @optional_callbacks config_description: 0, history_awareness: 0, id_filter: 1
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
@@ -0,0 +1,118 @@
+defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do
+ @moduledoc "Drop remote reports if they don't contain enough information."
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
+
+ alias Pleroma.Config
+
+ @impl true
+ 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
+ {:local, true} -> {:ok, object}
+ {:reject, message} -> {:reject, message}
+ error -> {:reject, error}
+ end
+ end
+
+ def filter(object), do: {:ok, object}
+
+ defp maybe_reject_all(object) do
+ if Config.get([:mrf_remote_report, :reject_all]) do
+ {:reject, "[RemoteReportPolicy] Remote report"}
+ else
+ {:ok, object}
+ end
+ end
+
+ defp maybe_reject_anonymous(%{"actor" => actor} = object) do
+ with true <- Config.get([:mrf_remote_report, :reject_anonymous]),
+ %URI{path: "/actor"} <- URI.parse(actor) do
+ {:reject, "[RemoteReportPolicy] Anonymous: #{actor}"}
+ else
+ _ -> {:ok, object}
+ 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}
+ end
+
+ defp maybe_reject_empty_message(object) do
+ if Config.get([:mrf_remote_report, :reject_empty_message]) do
+ {:reject, ["RemoteReportPolicy] No content"]}
+ else
+ {:ok, object}
+ end
+ end
+
+ defp local?(%{"actor" => actor}) do
+ String.starts_with?(actor, Pleroma.Web.Endpoint.url())
+ end
+
+ @impl true
+ def describe do
+ mrf_remote_report =
+ Config.get(:mrf_remote_report)
+ |> Enum.into(%{})
+
+ {:ok, %{mrf_remote_report: mrf_remote_report}}
+ end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_remote_report,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy",
+ label: "MRF Remote Report",
+ description: "Drop remote reports if they don't contain enough information.",
+ children: [
+ %{
+ key: :reject_all,
+ type: :boolean,
+ description: "Reject all remote reports? (this option takes precedence)",
+ suggestions: [false]
+ },
+ %{
+ key: :reject_anonymous,
+ type: :boolean,
+ 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,
+ description: "Reject remote reports with no message?",
+ suggestions: [true]
+ }
+ ]
+ }
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -192,6 +192,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
end
@impl true
+ def id_filter(id) do
+ host_info = URI.parse(id)
+
+ with {:ok, _} <- check_accept(host_info, %{}),
+ {:ok, _} <- check_reject(host_info, %{}) do
+ true
+ else
+ _ -> false
+ end
+ end
+
+ @impl true
def filter(%{"type" => "Delete", "actor" => actor} = activity) do
%{host: actor_host} = URI.parse(actor)
diff --git 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
@@ -38,6 +40,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/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -129,8 +129,22 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"vcard:bday" => birthday,
"webfinger" => "acct:#{User.full_nickname(user)}"
}
- |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
- |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
+ |> Map.merge(
+ maybe_make_image(
+ &User.avatar_url/2,
+ User.image_description(user.avatar, nil),
+ "icon",
+ user
+ )
+ )
+ |> Map.merge(
+ maybe_make_image(
+ &User.banner_url/2,
+ User.image_description(user.banner, nil),
+ "image",
+ user
+ )
+ )
|> Map.merge(Utils.make_json_ld_header())
end
@@ -305,16 +319,24 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
end
- defp maybe_make_image(func, key, user) do
+ defp maybe_make_image(func, description, key, user) do
if image = func.(user, no_default: true) do
%{
- key => %{
- "type" => "Image",
- "url" => image
- }
+ key =>
+ %{
+ "type" => "Image",
+ "url" => image
+ }
+ |> maybe_put_description(description)
}
else
%{}
end
end
+
+ defp maybe_put_description(map, description) when is_binary(description) do
+ Map.put(map, "name", description)
+ end
+
+ defp maybe_put_description(map, _description), do: map
end
diff --git a/lib/pleroma/web/api_spec/operations/media_operation.ex b/lib/pleroma/web/api_spec/operations/media_operation.ex
@@ -121,7 +121,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
security: [%{"oAuth" => ["write:media"]}],
requestBody: Helpers.request_body("Parameters", create_request()),
responses: %{
- 202 => Operation.response("Media", "application/json", Attachment),
+ 200 => Operation.response("Media", "application/json", Attachment),
400 => Operation.response("Media", "application/json", ApiError),
422 => Operation.response("Media", "application/json", ApiError),
500 => Operation.response("Media", "application/json", ApiError)
diff --git 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
@@ -30,4 +30,13 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator 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
@@ -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 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator 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
@@ -39,4 +39,8 @@ defmodule Pleroma.Web.Auth.WrapperAuthenticator 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
@@ -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 @@ defmodule Pleroma.Web.Feed.TagController 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/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex
@@ -53,9 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
) do
attachment_data = Map.put(object.data, "id", object.id)
- conn
- |> put_status(202)
- |> render("attachment.json", %{attachment: attachment_data})
+ render(conn, "attachment.json", %{attachment: attachment_data})
end
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
+ alias Pleroma.Workers.PollWorker
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
@@ -27,12 +28,16 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+ @poll_refresh_interval 120
@doc "GET /api/v1/polls/:id"
def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do
- with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
- %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
+ with %Object{} = object <- Object.get_by_id(id),
+ %Activity{} = activity <-
+ Activity.get_create_by_object_ap_id_with_object(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user) do
+ maybe_refresh_poll(activity)
+
try_render(conn, "show.json", %{object: object, for: user})
else
error when is_nil(error) or error == false ->
@@ -70,4 +75,13 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
end
end)
end
+
+ defp maybe_refresh_poll(%Activity{object: %Object{} = object} = activity) do
+ with false <- activity.local,
+ {:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]),
+ {_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)} do
+ PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id})
+ |> Oban.insert(unique: [period: @poll_refresh_interval])
+ end
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -219,10 +219,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
avatar = User.avatar_url(user) |> MediaProxy.url()
avatar_static = User.avatar_url(user) |> MediaProxy.preview_url(static: true)
- avatar_description = image_description(user.avatar)
+ avatar_description = User.image_description(user.avatar)
header = User.banner_url(user) |> MediaProxy.url()
header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true)
- header_description = image_description(user.banner)
+ header_description = User.image_description(user.banner)
following_count =
if !user.hide_follows_count or !user.hide_follows or self,
@@ -349,10 +349,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp username_from_nickname(_), do: nil
- defp image_description(%{"name" => name}), do: name
-
- defp image_description(_), do: ""
-
defp maybe_put_follow_requests_count(
data,
%User{id: user_id} = user,
diff --git 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 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
%{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
@@ -11,27 +11,46 @@ defmodule Pleroma.Workers.PollWorker do
alias Pleroma.Activity
alias Pleroma.Notification
alias Pleroma.Object
+ alias Pleroma.Object.Fetcher
+
+ @stream_out_impl Pleroma.Config.get(
+ [__MODULE__, :stream_out],
+ Pleroma.Web.ActivityPub.ActivityPub
+ )
@impl true
def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do
- with %Activity{} = activity <- find_poll_activity(activity_id),
+ with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)},
{:ok, notifications} <- Notification.create_poll_notifications(activity) do
+ unless activity.local do
+ # Schedule a final refresh
+ __MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id})
+ |> Oban.insert()
+ end
+
Notification.stream(notifications)
else
- {:error, :poll_activity_not_found} = e -> {:cancel, e}
+ {:activity, nil} -> {:cancel, :poll_activity_not_found}
e -> {:error, e}
end
end
- @impl true
- def timeout(_job), do: :timer.seconds(5)
+ def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do
+ with {_, %Activity{object: object}} <-
+ {:activity, Activity.get_by_id_with_object(activity_id)},
+ {_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do
+ stream_update(activity_id)
- defp find_poll_activity(activity_id) do
- with nil <- Activity.get_by_id(activity_id) do
- {:error, :poll_activity_not_found}
+ :ok
+ else
+ {:activity, nil} -> {:cancel, :poll_activity_not_found}
+ {:refetch, _} = e -> {:cancel, e}
end
end
+ @impl true
+ def timeout(_job), do: :timer.seconds(5)
+
def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <-
Object.normalize(activity),
@@ -49,4 +68,10 @@ defmodule Pleroma.Workers.PollWorker do
end
def schedule_poll_end(activity), do: {:error, activity}
+
+ defp stream_update(activity_id) do
+ Activity.get_by_id(activity_id)
+ |> Activity.normalize()
+ |> @stream_out_impl.stream_out()
+ end
end
diff --git a/mix.exs b/mix.exs
@@ -5,7 +5,7 @@ defmodule Pleroma.Mixfile do
[
app: :pleroma,
version: version("2.7.0"),
- elixir: "~> 1.13",
+ elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: Mix.compilers(),
elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false],
diff --git a/mix.lock b/mix.lock
@@ -92,7 +92,7 @@
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
- "oban": {:hex, :oban, "2.18.2", "583e78965ee15263ac968e38c983bad169ae55eadaa8e1e39912562badff93ba", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9dd25fd35883a91ed995e9fe516e479344d3a8623dfe2b8c3fc8e5be0228ec3a"},
+ "oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"},
"oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"},
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
"open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"},
diff --git a/rel/vm.args.eex b/rel/vm.args.eex
@@ -9,3 +9,8 @@
## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10
+
+# Disable wasteful busywait.
++sbwt none
++sbwtdcpu none
++sbwtdio none
diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs
@@ -6,12 +6,10 @@ defmodule Pleroma.ObjectTest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
- import ExUnit.CaptureLog
import Mox
import Pleroma.Factory
import Tesla.Mock
- alias Pleroma.Activity
alias Pleroma.Hashtag
alias Pleroma.Object
alias Pleroma.Repo
@@ -282,148 +280,6 @@ defmodule Pleroma.ObjectTest do
end
end
- describe "get_by_id_and_maybe_refetch" do
- setup do
- mock(fn
- %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
- %Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_original.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- }
-
- env ->
- apply(HttpRequestMock, :request, [env])
- end)
-
- mock_modified = fn resp ->
- mock(fn
- %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
- resp
-
- env ->
- apply(HttpRequestMock, :request, [env])
- end)
- end
-
- on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)
-
- [mock_modified: mock_modified]
- end
-
- test "refetches if the time since the last refetch is greater than the interval", %{
- mock_modified: mock_modified
- } do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- mock_modified.(%Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- })
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
- end
-
- test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- assert capture_log(fn ->
- mock_modified.(%Tesla.Env{status: 404, body: ""})
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
- end) =~
- "[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
- end
-
- test "does not refetch if the time since the last refetch is greater than the interval", %{
- mock_modified: mock_modified
- } do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- mock_modified.(%Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- })
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
- end
-
- test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- user = insert(:user)
- activity = Activity.get_create_by_object_ap_id(object.data["id"])
- {:ok, activity} = CommonAPI.favorite(activity.id, user)
- object = Object.get_by_ap_id(activity.data["object"])
-
- assert object.data["like_count"] == 1
-
- mock_modified.(%Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- })
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
-
- assert updated_object.data["like_count"] == 1
- end
- end
-
describe ":hashtags association" do
test "Hashtag records are created with Object record and updated on its change" do
user = insert(:user)
diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
@@ -1320,6 +1320,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest 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/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs
@@ -232,12 +232,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.avatar == %{
"type" => "Image",
- "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
+ "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
+ "name" => "profile picture"
}
assert user.banner == %{
"type" => "Image",
- "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
+ "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
+ "name" => "profile picture"
}
end
@@ -432,6 +434,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.birthday == ~D[2001-02-12]
end
+
+ test "fetches avatar description" do
+ user_id = "https://example.com/users/marcin"
+
+ user_data =
+ "test/fixtures/users_mock/user.json"
+ |> File.read!()
+ |> String.replace("{{nickname}}", "marcin")
+ |> Jason.decode!()
+ |> Map.delete("featured")
+ |> Map.update("icon", %{}, fn image -> Map.put(image, "name", "image description") end)
+ |> Jason.encode!()
+
+ Tesla.Mock.mock(fn
+ %{
+ method: :get,
+ url: ^user_id
+ } ->
+ %Tesla.Env{
+ status: 200,
+ body: user_data,
+ headers: [{"content-type", "application/activity+json"}]
+ }
+ end)
+
+ {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
+
+ assert user.avatar["name"] == "image description"
+ end
end
test "it fetches the appropriate tag-restricted posts" 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
@@ -0,0 +1,155 @@
+defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do
+ use Pleroma.DataCase, async: true
+
+ alias Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy
+
+ setup do
+ clear_config([:mrf_remote_report, :reject_all], false)
+ end
+
+ test "doesn't impact local report" do
+ clear_config([:mrf_remote_report, :reject_anonymous], true)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "http://localhost:4001/actor",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "rejects anonymous report if `reject_anonymous: true`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], true)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/actor",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "preserves anonymous report if `reject_anonymous: false`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "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)
+ end
+
+ test "rejects empty message report if `reject_empty_message: true`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "rejects empty message report (\"\") if `reject_empty_message: true`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["https://mastodon.online/users/Gargron"],
+ "content" => ""
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "preserves empty message report if `reject_empty_message: false`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], 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)
+ end
+
+ test "preserves anonymous, empty message report with all settings disabled" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/actor",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "reject remote report if `reject_all: true`" do
+ clear_config([:mrf_remote_report, :reject_all], true)
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "content" => "Transphobia",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
@@ -252,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "activity has a matching host" do
@@ -260,6 +261,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
+ refute SimplePolicy.id_filter(remote_message["actor"])
end
test "activity matches with wildcard domain" do
@@ -268,6 +270,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
+ refute SimplePolicy.id_filter(remote_message["actor"])
end
test "actor has a matching host" do
@@ -276,6 +279,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user()
assert {:reject, _} = SimplePolicy.filter(remote_user)
+ refute SimplePolicy.id_filter(remote_user["id"])
end
test "reject Announce when object would be rejected" do
@@ -288,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
}
assert {:reject, _} = SimplePolicy.filter(announce)
+ # Note: Non-Applicable for id_filter/1
end
test "reject by URI object" do
@@ -300,6 +305,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
}
assert {:reject, _} = SimplePolicy.filter(announce)
+ # Note: Non-Applicable for id_filter/1
end
end
@@ -370,6 +376,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(local_message["actor"])
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "is not empty but activity doesn't have a matching host" do
@@ -380,6 +388,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert {:reject, _} = SimplePolicy.filter(remote_message)
+ assert SimplePolicy.id_filter(local_message["actor"])
+ refute SimplePolicy.id_filter(remote_message["actor"])
end
test "activity has a matching host" do
@@ -390,6 +400,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(local_message["actor"])
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "activity matches with wildcard domain" do
@@ -400,6 +412,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(local_message["actor"])
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "actor has a matching host" do
@@ -408,6 +422,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
+ assert SimplePolicy.id_filter(remote_user["id"])
end
end
diff --git a/test/pleroma/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs
@@ -68,6 +68,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
result = UserView.render("user.json", %{user: user})
assert result["icon"]["url"] == "https://someurl"
assert result["image"]["url"] == "https://somebanner"
+
+ refute result["icon"]["name"]
+ refute result["image"]["name"]
+ end
+
+ test "Avatar has a description if the user set one" do
+ user =
+ insert(:user,
+ avatar: %{
+ "url" => [%{"href" => "https://someurl"}],
+ "name" => "a drawing of pleroma-tan using pleroma groups"
+ }
+ )
+
+ result = UserView.render("user.json", %{user: user})
+
+ assert result["icon"]["name"] == "a drawing of pleroma-tan using pleroma groups"
end
test "renders an invisible user with the invisible property set to true" do
diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs
@@ -191,4 +191,60 @@ defmodule Pleroma.Web.Feed.TagControllerTest 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/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
@@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v2/media", %{"file" => image, "description" => desc})
- |> json_response_and_validate_schema(202)
+ |> json_response_and_validate_schema(200)
assert media_id = response["id"]
@@ -111,7 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
"file" => large_binary,
"description" => desc
})
- |> json_response_and_validate_schema(202)
+ |> json_response_and_validate_schema(200)
assert media_id = response["id"]
diff --git a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
+ use Oban.Testing, repo: Pleroma.Repo
use Pleroma.Web.ConnCase, async: true
alias Pleroma.Object
@@ -27,6 +28,33 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
response = json_response_and_validate_schema(conn, 200)
id = to_string(object.id)
assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
+
+ # Local activities should not generate an Oban job to refresh
+ assert activity.local
+
+ refute_enqueued(
+ worker: Pleroma.Workers.PollWorker,
+ args: %{"op" => "refresh", "activity_id" => activity.id}
+ )
+ end
+
+ test "creates an oban job to refresh poll if activity is remote", %{conn: conn} do
+ user = insert(:user, local: false)
+ question = insert(:question, user: user)
+ activity = insert(:question_activity, question: question, local: false)
+
+ # Ensure this is not represented as a local activity
+ refute activity.local
+
+ object = Object.normalize(activity, fetch: false)
+
+ get(conn, "/api/v1/polls/#{object.id}")
+ |> json_response_and_validate_schema(200)
+
+ assert_enqueued(
+ worker: Pleroma.Workers.PollWorker,
+ args: %{"op" => "refresh", "activity_id" => activity.id}
+ )
end
test "does not expose polls for private statuses", %{conn: conn} do
diff --git 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)
@@ -44,6 +44,65 @@ defmodule Pleroma.Workers.PollWorkerTest do
# Ensure notifications were streamed out when job executes
assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], :_))
assert called(Pleroma.Web.Push.send(:_))
+
+ # 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)
+ activity = insert(:question_activity, question: question)
+
+ PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id})
+ |> Oban.insert()
+
+ expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"}
+
+ assert_enqueued(args: expected_job_args)
+
+ with_mocks([
+ {
+ Pleroma.Web.Streamer,
+ [],
+ [
+ stream: fn _, _ -> nil end
+ ]
+ }
+ ]) do
+ [job] = all_enqueued(worker: PollWorker)
+ PollWorker.perform(job)
+
+ # Ensure updates are streamed out
+ assert called(Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], :_))
end
end
end
diff --git a/test/support/factory.ex b/test/support/factory.ex
@@ -241,6 +241,7 @@ defmodule Pleroma.Factory do
def question_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user)
+ closed = attrs[:closed] || DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601()
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
@@ -251,7 +252,7 @@ defmodule Pleroma.Factory do
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [user.follower_address],
"context" => Pleroma.Web.ActivityPub.Utils.generate_context_id(),
- "closed" => DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601(),
+ "closed" => closed,
"content" => "Which flavor of ice cream do you prefer?",
"oneOf" => [
%{
@@ -509,7 +510,8 @@ defmodule Pleroma.Factory do
%Pleroma.Activity{
data: data,
actor: data["actor"],
- recipients: data["to"]
+ recipients: data["to"],
+ local: user.local
}
|> Map.merge(attrs)
end