Merge branch 'develop' into pleroma-token-view-scopes

This commit is contained in:
Lain Soykaf 2024-11-12 14:33:30 +04:00
commit 2baa9b0072
67 changed files with 825 additions and 278 deletions

View File

@ -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 variables: &global_variables
# Only used for the release # Only used for the release
ELIXIR_VER: 1.13.4 ELIXIR_VER: 1.14.5
POSTGRES_DB: pleroma_test POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
@ -71,7 +71,7 @@ check-changelog:
tags: tags:
- amd64 - amd64
build-1.13.4-otp-25: build-1.14.5-otp-25:
extends: extends:
- .build_changes_policy - .build_changes_policy
- .using-ci-base - .using-ci-base
@ -119,7 +119,7 @@ benchmark:
- mix ecto.migrate - mix ecto.migrate
- mix pleroma.load_testing - mix pleroma.load_testing
unit-testing-1.13.4-otp-25: unit-testing-1.14.5-otp-25:
extends: extends:
- .build_changes_policy - .build_changes_policy
- .using-ci-base - .using-ci-base

View File

@ -1,7 +1,8 @@
# https://hub.docker.com/r/hexpm/elixir/tags
ARG ELIXIR_IMG=hexpm/elixir ARG ELIXIR_IMG=hexpm/elixir
ARG ELIXIR_VER=1.13.4 ARG ELIXIR_VER=1.14.5
ARG ERLANG_VER=24.3.4.15 ARG ERLANG_VER=25.3.2.14
ARG ALPINE_VER=3.17.5 ARG ALPINE_VER=3.17.9
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build

View File

@ -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.

View File

View File

View File

@ -0,0 +1 @@
Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release

View File

View File

@ -0,0 +1 @@
Repesct :restrict_unauthenticated for hashtag rss/atom feeds

View File

@ -0,0 +1 @@
Fix incoming Block activities being rejected

View File

@ -0,0 +1 @@
LDAP now supports users changing their passwords

View File

@ -0,0 +1 @@
Fix /api/v2/media returning the wrong status code (202) for media processed synchronously

View File

@ -0,0 +1 @@
Add `id_filter` to MRF to filter URLs and their domain prior to fetching

View File

@ -0,0 +1 @@
Oban updated to 2.18.3

View File

@ -0,0 +1 @@
Poll results refreshing is handled asynchronously and will not attempt to keep fetching updates to a closed poll.

View File

@ -0,0 +1 @@
Tuning for release builds to lower CPU usage.

View File

@ -0,0 +1 @@
Added RemoteReportPolicy from Rebased for handling bogus federated reports

View File

@ -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 .

View File

@ -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

View File

@ -1,4 +1,4 @@
FROM elixir:1.12.3 FROM elixir:1.14.5-otp-25
# Single RUN statement, otherwise intermediate images are created # Single RUN statement, otherwise intermediate images are created
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run

View File

@ -1 +1 @@
docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25 --push . docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 --push .

View File

@ -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_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, config :pleroma, :mrf_force_mention,
mention_parent: true, mention_parent: true,
mention_quoted: true mention_quoted: true

View File

@ -69,12 +69,18 @@ cd /opt/pleroma
sudo -Hu pleroma mix deps.get 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`. * Answer with `yes` if it asks you to install `rebar3`.
* This may take some time, because parts of pleroma get compiled first. * 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`. * 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 ```shell
sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs} sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}

View File

@ -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 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
- `postgresql-contrib` 11.0以上 (同上) - `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-dev`
- `erlang-nox` - `erlang-nox`
- `git` - `git`

View File

@ -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)) ### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md))
```shell ```shell
# pkg install imagemagick ffmpeg p5-Image-ExifTool # pkg install imagemagick ffmpeg p5-Image-ExifTool vips
``` ```
## Configuring Pleroma ## Configuring Pleroma

View File

@ -1,8 +1,8 @@
## Required dependencies ## Required dependencies
* PostgreSQL >=11.0 * PostgreSQL >=11.0
* Elixir >=1.13.0 <1.17 * Elixir >=1.14.0 <1.17
* Erlang OTP >=22.2.0 (supported: <27) * Erlang OTP >=23.0.0 (supported: <27)
* git * git
* file / libmagic * file / libmagic
* gcc or clang * gcc or clang

View File

@ -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): 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. 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.

View File

@ -18,7 +18,7 @@ Matrix-kanava #pleroma:libera.chat ovat hyviä paikkoja löytää apua
Asenna tarvittava ohjelmisto: 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 #### Optional software

View File

@ -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

View File

@ -87,6 +87,7 @@ defmodule Pleroma.Constants do
const(activity_types, const(activity_types,
do: [ do: [
"Block",
"Create", "Create",
"Update", "Update",
"Delete", "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 # basic regex, just there to weed out potential mistakes
# https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 # https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
const(mime_regex, const(mime_regex,

View File

@ -15,6 +15,14 @@ defmodule Pleroma.LDAP do
GenServer.start_link(__MODULE__, [], name: __MODULE__) GenServer.start_link(__MODULE__, [], name: __MODULE__)
end 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 @impl true
def init(state) do def init(state) do
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
@ -47,13 +55,42 @@ defmodule Pleroma.LDAP do
def handle_info(: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 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) GenServer.reply(from, result)
{:noreply, state} {:noreply, state}
end 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 defp do_handle_connect do
state = state =
case connect() do case connect() do
@ -71,33 +108,6 @@ defmodule Pleroma.LDAP do
{:noreply, state} {:noreply, state}
end 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 defp connect do
ldap = Config.get(:ldap, []) ldap = Config.get(:ldap, [])
host = Keyword.get(ldap, :host, "localhost") host = Keyword.get(ldap, :host, "localhost")
@ -161,18 +171,17 @@ defmodule Pleroma.LDAP do
end end
end end
defp bind_user(handle, name, password) do defp do_bind_user(handle, name, password) do
uid = Config.get([:ldap, :uid], "cn") dn = make_dn(name)
base = Config.get([:ldap, :base])
case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do case :eldap.simple_bind(handle, dn, password) do
:ok -> :ok ->
case fetch_user(name) do case fetch_user(name) do
%User{} = user -> %User{} = user ->
user user
_ -> _ ->
register_user(handle, base, uid, name) register_user(handle, ldap_base(), ldap_uid(), name)
end end
# eldap does not inform us of socket closure # eldap does not inform us of socket closure
@ -231,6 +240,14 @@ defmodule Pleroma.LDAP do
end end
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 defp decode_certfile(file) do
with {:ok, data} <- File.read(file) do with {:ok, data} <- File.read(file) do
data data
@ -242,4 +259,13 @@ defmodule Pleroma.LDAP do
[] []
end end
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 end

View File

@ -99,27 +99,6 @@ defmodule Pleroma.Object do
def get_by_id(nil), do: nil def get_by_id(nil), do: nil
def get_by_id(id), do: Repo.get(Object, id) 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(nil), do: nil
def get_by_ap_id(ap_id) do def get_by_ap_id(ap_id) do

View File

@ -145,6 +145,7 @@ defmodule Pleroma.Object.Fetcher do
Logger.debug("Fetching object #{id} via AP") Logger.debug("Fetching object #{id} via AP")
with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")}, with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")},
{_, true} <- {:mrf, MRF.id_filter(id)},
{:ok, body} <- get_object(id), {:ok, body} <- get_object(id),
{:ok, data} <- safe_json_decode(body), {:ok, data} <- safe_json_decode(body),
:ok <- Containment.contain_origin_from_id(id, data) do :ok <- Containment.contain_origin_from_id(id, data) do
@ -160,6 +161,9 @@ defmodule Pleroma.Object.Fetcher do
{:error, e} -> {:error, e} ->
{:error, e} {:error, e}
{:mrf, false} ->
{:error, {:reject, "Filtered by id"}}
e -> e ->
{:error, e} {:error, e}
end end

View File

@ -419,6 +419,11 @@ defmodule Pleroma.User do
end end
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 # Should probably be renamed or removed
@spec ap_id(User.t()) :: String.t() @spec ap_id(User.t()) :: String.t()
def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}" def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"

View File

@ -1542,16 +1542,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp get_actor_url(_url), do: nil defp get_actor_url(_url), do: nil
defp normalize_image(%{"url" => url}) do defp normalize_image(%{"url" => url} = data) do
%{ %{
"type" => "Image", "type" => "Image",
"url" => [%{"href" => url}] "url" => [%{"href" => url}]
} }
|> maybe_put_description(data)
end end
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image() defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
defp normalize_image(_), do: nil 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 defp object_to_user_data(data, additional) do
fields = fields =
data data

View File

@ -108,6 +108,14 @@ defmodule Pleroma.Web.ActivityPub.MRF do
def filter(%{} = object), do: get_policies() |> filter(object) 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 @impl true
def pipeline_filter(%{} = message, meta) do def pipeline_filter(%{} = message, meta) do
object = meta[:object_data] object = meta[:object_data]

View File

@ -13,6 +13,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
{:reject, activity} {:reject, activity}
end end
@impl true
def id_filter(id) do
Logger.debug("REJECTING #{id}")
false
end
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}
end end

View File

@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.Policy do defmodule Pleroma.Web.ActivityPub.MRF.Policy do
@callback filter(Pleroma.Activity.t()) :: {:ok | :reject, Pleroma.Activity.t()} @callback filter(Pleroma.Activity.t()) :: {:ok | :reject, Pleroma.Activity.t()}
@callback id_filter(String.t()) :: boolean()
@callback describe() :: {:ok | :error, map()} @callback describe() :: {:ok | :error, map()}
@callback config_description() :: %{ @callback config_description() :: %{
optional(:children) => [map()], optional(:children) => [map()],
@ -13,5 +14,5 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do
description: String.t() description: String.t()
} }
@callback history_awareness() :: :auto | :manual @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 end

View File

@ -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

View File

@ -191,6 +191,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|> MRF.instance_list_from_tuples() |> MRF.instance_list_from_tuples()
end 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 @impl true
def filter(%{"type" => "Delete", "actor" => actor} = activity) do def filter(%{"type" => "Delete", "actor" => actor} = activity) do
%{host: actor_host} = URI.parse(actor) %{host: actor_host} = URI.parse(actor)

View File

@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
@behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating @behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
import Pleroma.Constants, only: [activity_types: 0, object_types: 0]
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object alias Pleroma.Object
@ -38,6 +40,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
@impl true @impl true
def validate(object, meta) 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 def validate(%{"type" => "Block"} = block_activity, meta) do
with {:ok, block_activity} <- with {:ok, block_activity} <-
block_activity block_activity

View File

@ -129,8 +129,22 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"vcard:bday" => birthday, "vcard:bday" => birthday,
"webfinger" => "acct:#{User.full_nickname(user)}" "webfinger" => "acct:#{User.full_nickname(user)}"
} }
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) |> Map.merge(
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) 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()) |> Map.merge(Utils.make_json_ld_header())
end end
@ -305,16 +319,24 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end end
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 if image = func.(user, no_default: true) do
%{ %{
key => %{ key =>
%{
"type" => "Image", "type" => "Image",
"url" => image "url" => image
} }
|> maybe_put_description(description)
} }
else else
%{} %{}
end end
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 end

View File

@ -121,7 +121,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
security: [%{"oAuth" => ["write:media"]}], security: [%{"oAuth" => ["write:media"]}],
requestBody: Helpers.request_body("Parameters", create_request()), requestBody: Helpers.request_body("Parameters", create_request()),
responses: %{ responses: %{
202 => Operation.response("Media", "application/json", Attachment), 200 => Operation.response("Media", "application/json", Attachment),
400 => Operation.response("Media", "application/json", ApiError), 400 => Operation.response("Media", "application/json", ApiError),
422 => Operation.response("Media", "application/json", ApiError), 422 => Operation.response("Media", "application/json", ApiError),
500 => Operation.response("Media", "application/json", ApiError) 500 => Operation.response("Media", "application/json", ApiError)

View File

@ -10,4 +10,9 @@ defmodule Pleroma.Web.Auth.Authenticator do
@callback handle_error(Plug.Conn.t(), any()) :: any() @callback handle_error(Plug.Conn.t(), any()) :: any()
@callback auth_template() :: String.t() | nil @callback auth_template() :: String.t() | nil
@callback oauth_consumer_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 end

View File

@ -30,4 +30,13 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
error error
end end
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 end

View File

@ -6,6 +6,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
alias Pleroma.Registration alias Pleroma.Registration
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.AuthenticationPlug alias Pleroma.Web.Plugs.AuthenticationPlug
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1] 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 auth_template, do: nil
def oauth_consumer_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 end

View File

@ -39,4 +39,8 @@ defmodule Pleroma.Web.Auth.WrapperAuthenticator do
implementation().oauth_consumer_template() || implementation().oauth_consumer_template() ||
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html") Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
end 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 end

View File

@ -10,7 +10,7 @@ defmodule Pleroma.Web.Feed.TagController do
alias Pleroma.Web.Feed.FeedView alias Pleroma.Web.Feed.FeedView
def feed(conn, params) do def feed(conn, params) do
if Config.get!([:instance, :public]) do if not Config.restrict_unauthenticated_access?(:timelines, :local) do
render_feed(conn, params) render_feed(conn, params)
else else
render_error(conn, :not_found, "Not found") render_error(conn, :not_found, "Not found")
@ -18,10 +18,12 @@ defmodule Pleroma.Web.Feed.TagController do
end end
defp render_feed(conn, %{"tag" => raw_tag} = params) do defp render_feed(conn, %{"tag" => raw_tag} = params) do
local_only = Config.restrict_unauthenticated_access?(:timelines, :federated)
{format, tag} = parse_tag(raw_tag) {format, tag} = parse_tag(raw_tag)
activities = activities =
%{type: ["Create"], tag: tag} %{type: ["Create"], tag: tag, local_only: local_only}
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"]) |> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|> ActivityPub.fetch_public_activities() |> ActivityPub.fetch_public_activities()

View File

@ -53,9 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
) do ) do
attachment_data = Map.put(object.data, "id", object.id) attachment_data = Map.put(object.data, "id", object.id)
conn render(conn, "attachment.json", %{attachment: attachment_data})
|> put_status(202)
|> render("attachment.json", %{attachment: attachment_data})
end end
end end

View File

@ -12,6 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Workers.PollWorker
action_fallback(Pleroma.Web.MastodonAPI.FallbackController) 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 defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation
@cachex Pleroma.Config.get([:cachex, :provider], Cachex) @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
@poll_refresh_interval 120
@doc "GET /api/v1/polls/:id" @doc "GET /api/v1/polls/:id"
def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do 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), with %Object{} = object <- Object.get_by_id(id),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), %Activity{} = activity <-
Activity.get_create_by_object_ap_id_with_object(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user) do true <- Visibility.visible_for_user?(activity, user) do
maybe_refresh_poll(activity)
try_render(conn, "show.json", %{object: object, for: user}) try_render(conn, "show.json", %{object: object, for: user})
else else
error when is_nil(error) or error == false -> error when is_nil(error) or error == false ->
@ -70,4 +75,13 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
end end
end) 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 end

View File

@ -219,10 +219,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
avatar = User.avatar_url(user) |> MediaProxy.url() avatar = User.avatar_url(user) |> MediaProxy.url()
avatar_static = User.avatar_url(user) |> MediaProxy.preview_url(static: true) 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 = User.banner_url(user) |> MediaProxy.url()
header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true) 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 = following_count =
if !user.hide_follows_count or !user.hide_follows or self, 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 username_from_nickname(_), do: nil
defp image_description(%{"name" => name}), do: name
defp image_description(_), do: ""
defp maybe_put_follow_requests_count( defp maybe_put_follow_requests_count(
data, data,
%User{id: user_id} = user, %User{id: user_id} = user,

View File

@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Healthcheck alias Pleroma.Healthcheck
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.WebFinger 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, %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
_ _
) do ) do
case CommonAPI.Utils.confirm_current_password(user, body_params.password) do with {:ok, %User{}} <-
{:ok, user} -> Authenticator.change_password(
with {:ok, _user} <- user,
User.reset_password(user, %{ body_params.password,
password: body_params.new_password, body_params.new_password,
password_confirmation: body_params.new_password_confirmation body_params.new_password_confirmation
}) do ) do
json(conn, %{status: "success"}) json(conn, %{status: "success"})
else else
{:error, changeset} -> {:error, %Ecto.Changeset{} = changeset} ->
{_, {error, _}} = Enum.at(changeset.errors, 0) {_, {error, _}} = Enum.at(changeset.errors, 0)
json(conn, %{error: "New password #{error}."}) json(conn, %{error: "New password #{error}."})
end
{:error, :password_confirmation} ->
json(conn, %{error: "New password does not match confirmation."})
{:error, msg} -> {:error, msg} ->
json(conn, %{error: msg}) json(conn, %{error: msg})

View File

@ -11,27 +11,46 @@ defmodule Pleroma.Workers.PollWorker do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Object.Fetcher
@stream_out_impl Pleroma.Config.get(
[__MODULE__, :stream_out],
Pleroma.Web.ActivityPub.ActivityPub
)
@impl true @impl true
def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do 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 {: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) Notification.stream(notifications)
else else
{:error, :poll_activity_not_found} = e -> {:cancel, e} {:activity, nil} -> {:cancel, :poll_activity_not_found}
e -> {:error, e} e -> {:error, e}
end end
end end
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)
:ok
else
{:activity, nil} -> {:cancel, :poll_activity_not_found}
{:refetch, _} = e -> {:cancel, e}
end
end
@impl true @impl true
def timeout(_job), do: :timer.seconds(5) def timeout(_job), do: :timer.seconds(5)
defp find_poll_activity(activity_id) do
with nil <- Activity.get_by_id(activity_id) do
{:error, :poll_activity_not_found}
end
end
def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <- with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <-
Object.normalize(activity), Object.normalize(activity),
@ -49,4 +68,10 @@ defmodule Pleroma.Workers.PollWorker do
end end
def schedule_poll_end(activity), do: {:error, activity} 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 end

View File

@ -5,7 +5,7 @@ defmodule Pleroma.Mixfile do
[ [
app: :pleroma, app: :pleroma,
version: version("2.7.0"), version: version("2.7.0"),
elixir: "~> 1.13", elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: Mix.compilers(), compilers: Mix.compilers(),
elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false], elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false],

View File

@ -92,7 +92,7 @@
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, "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"}, "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"}, "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"}, "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"},

View File

@ -9,3 +9,8 @@
## Tweak GC to run more often ## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10 ##-env ERL_FULLSWEEP_AFTER 10
# Disable wasteful busywait.
+sbwt none
+sbwtdcpu none
+sbwtdio none

View File

@ -6,12 +6,10 @@ defmodule Pleroma.ObjectTest do
use Pleroma.DataCase use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
import ExUnit.CaptureLog
import Mox import Mox
import Pleroma.Factory import Pleroma.Factory
import Tesla.Mock import Tesla.Mock
alias Pleroma.Activity
alias Pleroma.Hashtag alias Pleroma.Hashtag
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
@ -282,148 +280,6 @@ defmodule Pleroma.ObjectTest do
end end
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 describe ":hashtags association" do
test "Hashtag records are created with Object record and updated on its change" do test "Hashtag records are created with Object record and updated on its change" do
user = insert(:user) user = insert(:user)

View File

@ -1320,6 +1320,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
html_body: ~r/#{note.data["object"]}/i html_body: ~r/#{note.data["object"]}/i
) )
end 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 end
describe "GET /users/:nickname/outbox" do describe "GET /users/:nickname/outbox" do

View File

@ -232,12 +232,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.avatar == %{ assert user.avatar == %{
"type" => "Image", "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 == %{ assert user.banner == %{
"type" => "Image", "type" => "Image",
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}] "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
"name" => "profile picture"
} }
end end
@ -432,6 +434,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.birthday == ~D[2001-02-12] assert user.birthday == ~D[2001-02-12]
end 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 end
test "it fetches the appropriate tag-restricted posts" do test "it fetches the appropriate tag-restricted posts" do

View File

@ -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

View File

@ -252,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message() remote_message = build_remote_message()
assert SimplePolicy.filter(remote_message) == {:ok, remote_message} assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(remote_message["actor"])
end end
test "activity has a matching host" do test "activity has a matching host" do
@ -260,6 +261,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message() remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message) assert {:reject, _} = SimplePolicy.filter(remote_message)
refute SimplePolicy.id_filter(remote_message["actor"])
end end
test "activity matches with wildcard domain" do test "activity matches with wildcard domain" do
@ -268,6 +270,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message() remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message) assert {:reject, _} = SimplePolicy.filter(remote_message)
refute SimplePolicy.id_filter(remote_message["actor"])
end end
test "actor has a matching host" do test "actor has a matching host" do
@ -276,6 +279,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user() remote_user = build_remote_user()
assert {:reject, _} = SimplePolicy.filter(remote_user) assert {:reject, _} = SimplePolicy.filter(remote_user)
refute SimplePolicy.id_filter(remote_user["id"])
end end
test "reject Announce when object would be rejected" do 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) assert {:reject, _} = SimplePolicy.filter(announce)
# Note: Non-Applicable for id_filter/1
end end
test "reject by URI object" do test "reject by URI object" do
@ -300,6 +305,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
} }
assert {:reject, _} = SimplePolicy.filter(announce) assert {:reject, _} = SimplePolicy.filter(announce)
# Note: Non-Applicable for id_filter/1
end end
end end
@ -370,6 +376,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message} assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message} assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(local_message["actor"])
assert SimplePolicy.id_filter(remote_message["actor"])
end end
test "is not empty but activity doesn't have a matching host" do 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 SimplePolicy.filter(local_message) == {:ok, local_message}
assert {:reject, _} = SimplePolicy.filter(remote_message) assert {:reject, _} = SimplePolicy.filter(remote_message)
assert SimplePolicy.id_filter(local_message["actor"])
refute SimplePolicy.id_filter(remote_message["actor"])
end end
test "activity has a matching host" do 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(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message} assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(local_message["actor"])
assert SimplePolicy.id_filter(remote_message["actor"])
end end
test "activity matches with wildcard domain" do 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(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message} assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
assert SimplePolicy.id_filter(local_message["actor"])
assert SimplePolicy.id_filter(remote_message["actor"])
end end
test "actor has a matching host" do test "actor has a matching host" do
@ -408,6 +422,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user() remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user} assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
assert SimplePolicy.id_filter(remote_user["id"])
end end
end end

View File

@ -68,6 +68,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
result = UserView.render("user.json", %{user: user}) result = UserView.render("user.json", %{user: user})
assert result["icon"]["url"] == "https://someurl" assert result["icon"]["url"] == "https://someurl"
assert result["image"]["url"] == "https://somebanner" 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 end
test "renders an invisible user with the invisible property set to true" do test "renders an invisible user with the invisible property set to true" do

View File

@ -191,4 +191,60 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|> response(404) |> response(404)
end end
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 end

View File

@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
conn conn
|> put_req_header("content-type", "multipart/form-data") |> put_req_header("content-type", "multipart/form-data")
|> post("/api/v2/media", %{"file" => image, "description" => desc}) |> 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"] assert media_id = response["id"]
@ -111,7 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
"file" => large_binary, "file" => large_binary,
"description" => desc "description" => desc
}) })
|> json_response_and_validate_schema(202) |> json_response_and_validate_schema(200)
assert media_id = response["id"] assert media_id = response["id"]

View File

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.PollControllerTest do defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase, async: true
alias Pleroma.Object alias Pleroma.Object
@ -27,6 +28,33 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
response = json_response_and_validate_schema(conn, 200) response = json_response_and_validate_schema(conn, 200)
id = to_string(object.id) id = to_string(object.id)
assert %{"id" => ^id, "expired" => false, "multiple" => false} = response 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 end
test "does not expose polls for private statuses", %{conn: conn} do test "does not expose polls for private statuses", %{conn: conn} do

View File

@ -11,10 +11,10 @@ defmodule Pleroma.Workers.PollWorkerTest do
alias Pleroma.Workers.PollWorker alias Pleroma.Workers.PollWorker
test "poll notification job" do test "local poll ending notification job" do
user = insert(:user) user = insert(:user)
question = insert(:question, user: 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) PollWorker.schedule_poll_end(activity)
@ -44,6 +44,65 @@ defmodule Pleroma.Workers.PollWorkerTest do
# Ensure notifications were streamed out when job executes # Ensure notifications were streamed out when job executes
assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], :_)) assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], :_))
assert called(Pleroma.Web.Push.send(:_)) 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 end
end end

View File

@ -241,6 +241,7 @@ defmodule Pleroma.Factory do
def question_factory(attrs \\ %{}) do def question_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user) user = attrs[:user] || insert(:user)
closed = attrs[:closed] || DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601()
data = %{ data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
@ -251,7 +252,7 @@ defmodule Pleroma.Factory do
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [user.follower_address], "cc" => [user.follower_address],
"context" => Pleroma.Web.ActivityPub.Utils.generate_context_id(), "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?", "content" => "Which flavor of ice cream do you prefer?",
"oneOf" => [ "oneOf" => [
%{ %{
@ -509,7 +510,8 @@ defmodule Pleroma.Factory do
%Pleroma.Activity{ %Pleroma.Activity{
data: data, data: data,
actor: data["actor"], actor: data["actor"],
recipients: data["to"] recipients: data["to"],
local: user.local
} }
|> Map.merge(attrs) |> Map.merge(attrs)
end end