summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changelog.d/argon2-passwords.add1
-rw-r--r--changelog.d/dialyzer.skip0
-rw-r--r--changelog.d/docs-fix.skip0
-rw-r--r--changelog.d/drop-unwanted.change1
-rw-r--r--changelog.d/following-state.fix1
-rw-r--r--changelog.d/ldap-tls.fix1
-rw-r--r--changelog.d/list-id-visibility.add1
-rw-r--r--changelog.d/manifest-icon-size.skip0
-rw-r--r--changelog.d/mogrify.skip0
-rw-r--r--changelog.d/oauth-app-spam.fix1
-rw-r--r--changelog.d/oban-recevier-improvements.fix1
-rw-r--r--changelog.d/oban-uniques.change1
-rw-r--r--changelog.d/publisher-reachability.fix1
-rw-r--r--changelog.d/remote-object-fetcher.fix1
-rw-r--r--changelog.d/rich-media-no-heads.change1
-rw-r--r--changelog.d/scrubbers-allow-mention-hashtag.add1
-rw-r--r--changelog.d/todo-cleanup.skip0
-rw-r--r--changelog.d/update-oban.change1
-rw-r--r--changelog.d/user-imports.fix1
-rw-r--r--changelog.d/well-known.change1
-rw-r--r--config/config.exs6
-rw-r--r--docs/development/API/admin_api.md31
-rw-r--r--docs/development/API/differences_in_mastoapi_responses.md1
-rw-r--r--lib/pleroma/constants.ex30
-rw-r--r--lib/pleroma/http/adapter_helper.ex10
-rw-r--r--lib/pleroma/http/adapter_helper/finch.ex33
-rw-r--r--lib/pleroma/http/adapter_helper/gun.ex9
-rw-r--r--lib/pleroma/maps.ex4
-rw-r--r--lib/pleroma/object/fetcher.ex44
-rw-r--r--lib/pleroma/user/backup.ex17
-rw-r--r--lib/pleroma/user/import.ex144
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex2
-rw-r--r--lib/pleroma/web/activity_pub/pipeline.ex19
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex20
-rw-r--r--lib/pleroma/web/api_spec/schemas/status.ex6
-rw-r--r--lib/pleroma/web/auth/ldap_authenticator.ex12
-rw-r--r--lib/pleroma/web/common_api.ex44
-rw-r--r--lib/pleroma/web/federator.ex8
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/app_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex19
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex27
-rw-r--r--lib/pleroma/web/o_auth/app.ex26
-rw-r--r--lib/pleroma/web/o_auth/o_auth_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex8
-rw-r--r--lib/pleroma/web/plugs/authentication_plug.ex9
-rw-r--r--lib/pleroma/web/plugs/inbox_guard_plug.ex89
-rw-r--r--lib/pleroma/web/rich_media/helpers.ex71
-rw-r--r--lib/pleroma/web/router.ex8
-rw-r--r--lib/pleroma/workers/background_worker.ex6
-rw-r--r--lib/pleroma/workers/cron/app_cleanup_worker.ex21
-rw-r--r--lib/pleroma/workers/receiver_worker.ex26
-rw-r--r--lib/pleroma/workers/remote_fetcher_worker.ex25
-rw-r--r--lib/pleroma/workers/rich_media_worker.ex2
-rw-r--r--lib/pleroma/workers/user_refresh_worker.ex2
-rw-r--r--lib/pleroma/workers/web_pusher_worker.ex2
-rw-r--r--mix.exs6
-rw-r--r--mix.lock9
-rw-r--r--priv/gettext/zh_Hans/LC_MESSAGES/config_descriptions.po228
-rw-r--r--priv/gettext/zh_Hans/LC_MESSAGES/default.po16
-rw-r--r--priv/gettext/zh_Hans/LC_MESSAGES/errors.po8
-rw-r--r--priv/gettext/zh_Hans/LC_MESSAGES/oauth_scopes.po274
-rw-r--r--priv/gettext/zh_Hans/LC_MESSAGES/posix_errors.po48
-rw-r--r--priv/repo/migrations/20240904142434_assign_app_user.exs21
-rw-r--r--priv/scrubbers/default.ex3
-rw-r--r--priv/scrubbers/twitter_text.ex3
-rw-r--r--test/fixtures/bastianallgeier.json117
-rw-r--r--test/fixtures/receiver_worker_signature_activity.json127
-rw-r--r--test/pleroma/html_test.exs22
-rw-r--r--test/pleroma/object/fetcher_test.exs15
-rw-r--r--test/pleroma/user/import_test.exs27
-rw-r--r--test/pleroma/web/activity_pub/activity_pub_controller_test.exs50
-rw-r--r--test/pleroma/web/activity_pub/publisher_test.exs4
-rw-r--r--test/pleroma/web/mastodon_api/views/account_view_test.exs39
-rw-r--r--test/pleroma/web/mastodon_api/views/status_view_test.exs8
-rw-r--r--test/pleroma/web/node_info_test.exs13
-rw-r--r--test/pleroma/web/o_auth/app_test.exs17
-rw-r--r--test/pleroma/web/o_auth/o_auth_controller_test.exs8
-rw-r--r--test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs92
-rw-r--r--test/pleroma/web/plugs/authentication_plug_test.exs26
-rw-r--r--test/pleroma/workers/receiver_worker_test.exs288
-rw-r--r--test/pleroma/workers/remote_fetcher_worker_test.exs45
81 files changed, 1563 insertions, 751 deletions
diff --git a/changelog.d/argon2-passwords.add b/changelog.d/argon2-passwords.add
new file mode 100644
index 000000000..36fd7faf2
--- /dev/null
+++ b/changelog.d/argon2-passwords.add
@@ -0,0 +1 @@
+Added support for argon2 passwords and their conversion for migration from Akkoma fork to upstream.
diff --git a/changelog.d/dialyzer.skip b/changelog.d/dialyzer.skip
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/changelog.d/dialyzer.skip
diff --git a/changelog.d/docs-fix.skip b/changelog.d/docs-fix.skip
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/changelog.d/docs-fix.skip
diff --git a/changelog.d/drop-unwanted.change b/changelog.d/drop-unwanted.change
new file mode 100644
index 000000000..459d4bfe6
--- /dev/null
+++ b/changelog.d/drop-unwanted.change
@@ -0,0 +1 @@
+Restrict incoming activities from unknown actors to a subset that does not imply a previous relationship and early rejection of unrecognized activity types.
diff --git a/changelog.d/following-state.fix b/changelog.d/following-state.fix
new file mode 100644
index 000000000..314ea6210
--- /dev/null
+++ b/changelog.d/following-state.fix
@@ -0,0 +1 @@
+Resolved edge case where the API can report you are following a user but the relationship is not fully established.
diff --git a/changelog.d/ldap-tls.fix b/changelog.d/ldap-tls.fix
new file mode 100644
index 000000000..b15137d77
--- /dev/null
+++ b/changelog.d/ldap-tls.fix
@@ -0,0 +1 @@
+STARTTLS certificate and hostname verification for LDAP authentication
diff --git a/changelog.d/list-id-visibility.add b/changelog.d/list-id-visibility.add
new file mode 100644
index 000000000..2fea2d771
--- /dev/null
+++ b/changelog.d/list-id-visibility.add
@@ -0,0 +1 @@
+Include list id in StatusView \ No newline at end of file
diff --git a/changelog.d/manifest-icon-size.skip b/changelog.d/manifest-icon-size.skip
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/changelog.d/manifest-icon-size.skip
diff --git a/changelog.d/mogrify.skip b/changelog.d/mogrify.skip
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/changelog.d/mogrify.skip
diff --git a/changelog.d/oauth-app-spam.fix b/changelog.d/oauth-app-spam.fix
new file mode 100644
index 000000000..cdc2e816d
--- /dev/null
+++ b/changelog.d/oauth-app-spam.fix
@@ -0,0 +1 @@
+Add a rate limiter to the OAuth App creation endpoint and ensure registered apps are assigned to users.
diff --git a/changelog.d/oban-recevier-improvements.fix b/changelog.d/oban-recevier-improvements.fix
new file mode 100644
index 000000000..f91502ed2
--- /dev/null
+++ b/changelog.d/oban-recevier-improvements.fix
@@ -0,0 +1 @@
+ReceiverWorker will cancel processing jobs instead of retrying if the user cannot be fetched due to 403, 404, or 410 errors or if the account is disabled locally.
diff --git a/changelog.d/oban-uniques.change b/changelog.d/oban-uniques.change
new file mode 100644
index 000000000..d9deb4696
--- /dev/null
+++ b/changelog.d/oban-uniques.change
@@ -0,0 +1 @@
+Adjust more Oban workers to enforce unique job constraints.
diff --git a/changelog.d/publisher-reachability.fix b/changelog.d/publisher-reachability.fix
new file mode 100644
index 000000000..3f50be581
--- /dev/null
+++ b/changelog.d/publisher-reachability.fix
@@ -0,0 +1 @@
+Address case where instance reachability status couldn't be updated
diff --git a/changelog.d/remote-object-fetcher.fix b/changelog.d/remote-object-fetcher.fix
new file mode 100644
index 000000000..dcf2b1b31
--- /dev/null
+++ b/changelog.d/remote-object-fetcher.fix
@@ -0,0 +1 @@
+Remote Fetcher Worker recognizes more permanent failure errors
diff --git a/changelog.d/rich-media-no-heads.change b/changelog.d/rich-media-no-heads.change
new file mode 100644
index 000000000..0bab323aa
--- /dev/null
+++ b/changelog.d/rich-media-no-heads.change
@@ -0,0 +1 @@
+Rich Media preview fetching will skip making an HTTP HEAD request to check a URL for allowed content type and length if the Tesla adapter is Gun or Finch
diff --git a/changelog.d/scrubbers-allow-mention-hashtag.add b/changelog.d/scrubbers-allow-mention-hashtag.add
new file mode 100644
index 000000000..c12ab1ffb
--- /dev/null
+++ b/changelog.d/scrubbers-allow-mention-hashtag.add
@@ -0,0 +1 @@
+scrubbers/default: Allow "mention hashtag" classes used by Mastodon \ No newline at end of file
diff --git a/changelog.d/todo-cleanup.skip b/changelog.d/todo-cleanup.skip
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/changelog.d/todo-cleanup.skip
diff --git a/changelog.d/update-oban.change b/changelog.d/update-oban.change
new file mode 100644
index 000000000..a67b3e3cf
--- /dev/null
+++ b/changelog.d/update-oban.change
@@ -0,0 +1 @@
+Update Oban to 2.18
diff --git a/changelog.d/user-imports.fix b/changelog.d/user-imports.fix
new file mode 100644
index 000000000..0076c73d7
--- /dev/null
+++ b/changelog.d/user-imports.fix
@@ -0,0 +1 @@
+Imports of blocks, mutes, and follows would retry repeatedly due to incorrect error handling and all work executed in a single job
diff --git a/changelog.d/well-known.change b/changelog.d/well-known.change
new file mode 100644
index 000000000..e928124fb
--- /dev/null
+++ b/changelog.d/well-known.change
@@ -0,0 +1 @@
+Accept application/activity+json for requests to .well-known/nodeinfo
diff --git a/config/config.exs b/config/config.exs
index ad6b1cb94..cd9a2539f 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -344,7 +344,7 @@ config :pleroma, :manifest,
icons: [
%{
src: "/static/logo.svg",
- sizes: "144x144",
+ sizes: "512x512",
purpose: "any",
type: "image/svg+xml"
}
@@ -597,7 +597,8 @@ config :pleroma, Oban,
plugins: [{Oban.Plugins.Pruner, max_age: 900}],
crontab: [
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
- {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
+ {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker},
+ {"*/10 * * * *", Pleroma.Workers.Cron.AppCleanupWorker}
]
config :pleroma, Pleroma.Formatter,
@@ -711,6 +712,7 @@ config :pleroma, :rate_limit,
timeline: {500, 3},
search: [{1000, 10}, {1000, 30}],
app_account_creation: {1_800_000, 25},
+ oauth_app_creation: {900_000, 5},
relations_actions: {10_000, 10},
relation_id_action: {60_000, 2},
statuses_actions: {10_000, 15},
diff --git a/docs/development/API/admin_api.md b/docs/development/API/admin_api.md
index 5b373b8e1..409e78a1e 100644
--- a/docs/development/API/admin_api.md
+++ b/docs/development/API/admin_api.md
@@ -433,7 +433,7 @@ Response:
* On success: URL of the unfollowed relay
```json
-{"https://example.com/relay"}
+"https://example.com/relay"
```
## `POST /api/v1/pleroma/admin/users/invite_token`
@@ -1193,20 +1193,23 @@ Loads json generated from `config/descriptions.exs`.
- Response:
```json
-[
- {
- "id": 1234,
- "data": {
- "actor": {
- "id": 1,
- "nickname": "lain"
+{
+ "items": [
+ {
+ "id": 1234,
+ "data": {
+ "actor": {
+ "id": 1,
+ "nickname": "lain"
+ },
+ "action": "relay_follow"
},
- "action": "relay_follow"
- },
- "time": 1502812026, // timestamp
- "message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
- }
-]
+ "time": 1502812026, // timestamp
+ "message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
+ }
+ ],
+ "total": 1
+}
```
## `POST /api/v1/pleroma/admin/reload_emoji`
diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md
index 22a26b77b..cbd0d6bce 100644
--- a/docs/development/API/differences_in_mastoapi_responses.md
+++ b/docs/development/API/differences_in_mastoapi_responses.md
@@ -42,6 +42,7 @@ Has these additional fields under the `pleroma` object:
- `quotes_count`: the count of status quotes.
- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen.
- `bookmark_folder`: the ID of the folder bookmark is stored within (if any).
+- `list_id`: the ID of the list the post is addressed to (if any, only returned to author).
The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes:
diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex
index 3a5e35301..5268ebe7a 100644
--- a/lib/pleroma/constants.ex
+++ b/lib/pleroma/constants.ex
@@ -85,6 +85,36 @@ defmodule Pleroma.Constants do
]
)
+ const(activity_types,
+ do: [
+ "Create",
+ "Update",
+ "Delete",
+ "Follow",
+ "Accept",
+ "Reject",
+ "Add",
+ "Remove",
+ "Like",
+ "Announce",
+ "Undo",
+ "Flag",
+ "EmojiReact"
+ ]
+ )
+
+ const(allowed_activity_types_from_strangers,
+ do: [
+ "Block",
+ "Create",
+ "Flag",
+ "Follow",
+ "Like",
+ "EmojiReact",
+ "Announce"
+ ]
+ )
+
# 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/http/adapter_helper.ex b/lib/pleroma/http/adapter_helper.ex
index dcb27a29d..32c1080f7 100644
--- a/lib/pleroma/http/adapter_helper.ex
+++ b/lib/pleroma/http/adapter_helper.ex
@@ -52,6 +52,7 @@ defmodule Pleroma.HTTP.AdapterHelper do
case adapter() do
Tesla.Adapter.Gun -> AdapterHelper.Gun
Tesla.Adapter.Hackney -> AdapterHelper.Hackney
+ {Tesla.Adapter.Finch, _} -> AdapterHelper.Finch
_ -> AdapterHelper.Default
end
end
@@ -118,4 +119,13 @@ defmodule Pleroma.HTTP.AdapterHelper do
host_charlist
end
end
+
+ @spec can_stream? :: bool()
+ def can_stream? do
+ case Application.get_env(:tesla, :adapter) do
+ Tesla.Adapter.Gun -> true
+ {Tesla.Adapter.Finch, _} -> true
+ _ -> false
+ end
+ end
end
diff --git a/lib/pleroma/http/adapter_helper/finch.ex b/lib/pleroma/http/adapter_helper/finch.ex
new file mode 100644
index 000000000..181caed7e
--- /dev/null
+++ b/lib/pleroma/http/adapter_helper/finch.ex
@@ -0,0 +1,33 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.HTTP.AdapterHelper.Finch do
+ @behaviour Pleroma.HTTP.AdapterHelper
+
+ alias Pleroma.Config
+ alias Pleroma.HTTP.AdapterHelper
+
+ @spec options(keyword(), URI.t()) :: keyword()
+ def options(incoming_opts \\ [], %URI{} = _uri) do
+ proxy =
+ [:http, :proxy_url]
+ |> Config.get()
+ |> AdapterHelper.format_proxy()
+
+ config_opts = Config.get([:http, :adapter], [])
+
+ config_opts
+ |> Keyword.merge(incoming_opts)
+ |> AdapterHelper.maybe_add_proxy(proxy)
+ |> maybe_stream()
+ end
+
+ # Finch uses [response: :stream]
+ defp maybe_stream(opts) do
+ case Keyword.pop(opts, :stream, nil) do
+ {true, opts} -> Keyword.put(opts, :response, :stream)
+ {_, opts} -> opts
+ end
+ end
+end
diff --git a/lib/pleroma/http/adapter_helper/gun.ex b/lib/pleroma/http/adapter_helper/gun.ex
index 1fe8dd4b2..30ba26765 100644
--- a/lib/pleroma/http/adapter_helper/gun.ex
+++ b/lib/pleroma/http/adapter_helper/gun.ex
@@ -32,6 +32,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
|> AdapterHelper.maybe_add_proxy(proxy)
|> Keyword.merge(incoming_opts)
|> put_timeout()
+ |> maybe_stream()
end
defp add_scheme_opts(opts, %{scheme: "http"}), do: opts
@@ -47,6 +48,14 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
Keyword.put(opts, :timeout, recv_timeout)
end
+ # Gun uses [body_as: :stream]
+ defp maybe_stream(opts) do
+ case Keyword.pop(opts, :stream, nil) do
+ {true, opts} -> Keyword.put(opts, :body_as, :stream)
+ {_, opts} -> opts
+ end
+ end
+
@spec pool_timeout(pool()) :: non_neg_integer()
def pool_timeout(pool) do
default = Config.get([:pools, :default, :recv_timeout], 5_000)
diff --git a/lib/pleroma/maps.ex b/lib/pleroma/maps.ex
index 5020a8ff8..1afbde484 100644
--- a/lib/pleroma/maps.ex
+++ b/lib/pleroma/maps.ex
@@ -20,15 +20,13 @@ defmodule Pleroma.Maps do
end
def filter_empty_values(data) do
- # TODO: Change to Map.filter in Elixir 1.13+
data
- |> Enum.filter(fn
+ |> Map.filter(fn
{_k, nil} -> false
{_k, ""} -> false
{_k, []} -> false
{_k, %{} = v} -> Map.keys(v) != []
{_k, _v} -> true
end)
- |> Map.new()
end
end
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index c0f671dd4..69a5f3268 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -58,8 +58,12 @@ defmodule Pleroma.Object.Fetcher do
end
end
+ @typep fetcher_errors ::
+ :error | :reject | :allowed_depth | :fetch | :containment | :transmogrifier
+
# Note: will create a Create activity, which we need internally at the moment.
- @spec fetch_object_from_id(String.t(), list()) :: {:ok, Object.t()} | {:error | :reject, any()}
+ @spec fetch_object_from_id(String.t(), list()) ::
+ {:ok, Object.t()} | {fetcher_errors(), any()} | Pipeline.errors()
def fetch_object_from_id(id, options \\ []) do
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
@@ -73,50 +77,22 @@ defmodule Pleroma.Object.Fetcher do
{:object, data, Object.normalize(activity, fetch: false)} do
{:ok, object}
else
- {:allowed_depth, false} = e ->
- log_fetch_error(id, e)
- {:error, :allowed_depth}
-
- {:containment, reason} = e ->
- log_fetch_error(id, e)
- {:error, reason}
-
- {:transmogrifier, {:error, {:reject, reason}}} = e ->
- log_fetch_error(id, e)
- {:reject, reason}
-
- {:transmogrifier, {:reject, reason}} = e ->
- log_fetch_error(id, e)
- {:reject, reason}
-
- {:transmogrifier, reason} = e ->
- log_fetch_error(id, e)
- {:error, reason}
-
- {:object, data, nil} ->
- reinject_object(%Object{}, data)
-
{:normalize, object = %Object{}} ->
{:ok, object}
{:fetch_object, %Object{} = object} ->
{:ok, object}
- {:fetch, {:error, reason}} = e ->
- log_fetch_error(id, e)
- {:error, reason}
+ {:object, data, nil} ->
+ reinject_object(%Object{}, data)
e ->
- log_fetch_error(id, e)
- {:error, e}
+ Logger.metadata(object: id)
+ Logger.error("Object rejected while fetching #{id} #{inspect(e)}")
+ e
end
end
- defp log_fetch_error(id, error) do
- Logger.metadata(object: id)
- Logger.error("Object rejected while fetching #{id} #{inspect(error)}")
- end
-
defp prepare_activity_params(data) do
%{
"type" => "Create",
diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex
index 7feaa22bf..d77d49890 100644
--- a/lib/pleroma/user/backup.ex
+++ b/lib/pleroma/user/backup.ex
@@ -92,9 +92,6 @@ defmodule Pleroma.User.Backup do
else
true ->
{:error, "Backup is missing id. Please insert it into the Repo first."}
-
- e ->
- {:error, e}
end
end
@@ -121,14 +118,13 @@ defmodule Pleroma.User.Backup do
end
defp permitted?(user) do
- with {_, %__MODULE__{inserted_at: inserted_at}} <- {:last, get_last(user)},
- days = Config.get([__MODULE__, :limit_days]),
- diff = Timex.diff(NaiveDateTime.utc_now(), inserted_at, :days),
- {_, true} <- {:diff, diff > days} do
- true
+ with {_, %__MODULE__{inserted_at: inserted_at}} <- {:last, get_last(user)} do
+ days = Config.get([__MODULE__, :limit_days])
+ diff = Timex.diff(NaiveDateTime.utc_now(), inserted_at, :days)
+
+ diff > days
else
{:last, nil} -> true
- {:diff, false} -> false
end
end
@@ -297,9 +293,6 @@ defmodule Pleroma.User.Backup do
)
acc
-
- _ ->
- acc
end
end)
diff --git a/lib/pleroma/user/import.ex b/lib/pleroma/user/import.ex
index 11905237c..ab6bdb8d4 100644
--- a/lib/pleroma/user/import.ex
+++ b/lib/pleroma/user/import.ex
@@ -5,87 +5,107 @@
defmodule Pleroma.User.Import do
use Ecto.Schema
+ alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Workers.BackgroundWorker
require Logger
- @spec perform(atom(), User.t(), list()) :: :ok | list() | {:error, any()}
- def perform(:mutes_import, %User{} = user, [_ | _] = identifiers) do
- Enum.map(
- identifiers,
- fn identifier ->
- with {:ok, %User{} = muted_user} <- User.get_or_fetch(identifier),
- {:ok, _} <- User.mute(user, muted_user) do
- muted_user
- else
- error -> handle_error(:mutes_import, identifier, error)
- end
- end
- )
+ @spec perform(atom(), User.t(), String.t()) :: :ok | {:error, any()}
+ def perform(:mute_import, %User{} = user, actor) do
+ with {:ok, %User{} = muted_user} <- User.get_or_fetch(actor),
+ {_, false} <- {:existing_mute, User.mutes_user?(user, muted_user)},
+ {:ok, _} <- User.mute(user, muted_user) do
+ {:ok, muted_user}
+ else
+ {:existing_mute, true} -> :ok
+ error -> handle_error(:mutes_import, actor, error)
+ end
end
- def perform(:blocks_import, %User{} = blocker, [_ | _] = identifiers) do
- Enum.map(
- identifiers,
- fn identifier ->
- with {:ok, %User{} = blocked} <- User.get_or_fetch(identifier),
- {:ok, _block} <- CommonAPI.block(blocked, blocker) do
- blocked
- else
- error -> handle_error(:blocks_import, identifier, error)
- end
- end
- )
+ def perform(:block_import, %User{} = user, actor) do
+ with {:ok, %User{} = blocked} <- User.get_or_fetch(actor),
+ {_, false} <- {:existing_block, User.blocks_user?(user, blocked)},
+ {:ok, _block} <- CommonAPI.block(blocked, user) do
+ {:ok, blocked}
+ else
+ {:existing_block, true} -> :ok
+ error -> handle_error(:blocks_import, actor, error)
+ end
end
- def perform(:follow_import, %User{} = follower, [_ | _] = identifiers) do
- Enum.map(
- identifiers,
- fn identifier ->
- with {:ok, %User{} = followed} <- User.get_or_fetch(identifier),
- {:ok, follower, followed} <- User.maybe_direct_follow(follower, followed),
- {:ok, _, _, _} <- CommonAPI.follow(followed, follower) do
- followed
- else
- error -> handle_error(:follow_import, identifier, error)
- end
- end
- )
+ def perform(:follow_import, %User{} = user, actor) do
+ with {:ok, %User{} = followed} <- User.get_or_fetch(actor),
+ {_, false} <- {:existing_follow, User.following?(user, followed)},
+ {:ok, user, followed} <- User.maybe_direct_follow(user, followed),
+ {:ok, _, _, _} <- CommonAPI.follow(followed, user) do
+ {:ok, followed}
+ else
+ {:existing_follow, true} -> :ok
+ error -> handle_error(:follow_import, actor, error)
+ end
end
- def perform(_, _, _), do: :ok
-
defp handle_error(op, user_id, error) do
Logger.debug("#{op} failed for #{user_id} with: #{inspect(error)}")
- error
+ {:error, error}
end
- def blocks_import(%User{} = blocker, [_ | _] = identifiers) do
- BackgroundWorker.new(%{
- "op" => "blocks_import",
- "user_id" => blocker.id,
- "identifiers" => identifiers
- })
- |> Oban.insert()
+ def blocks_import(%User{} = user, [_ | _] = actors) do
+ jobs =
+ Repo.checkout(fn ->
+ Enum.reduce(actors, [], fn actor, acc ->
+ {:ok, job} =
+ BackgroundWorker.new(%{
+ "op" => "block_import",
+ "user_id" => user.id,
+ "actor" => actor
+ })
+ |> Oban.insert()
+
+ acc ++ [job]
+ end)
+ end)
+
+ {:ok, jobs}
end
- def follow_import(%User{} = follower, [_ | _] = identifiers) do
- BackgroundWorker.new(%{
- "op" => "follow_import",
- "user_id" => follower.id,
- "identifiers" => identifiers
- })
- |> Oban.insert()
+ def follows_import(%User{} = user, [_ | _] = actors) do
+ jobs =
+ Repo.checkout(fn ->
+ Enum.reduce(actors, [], fn actor, acc ->
+ {:ok, job} =
+ BackgroundWorker.new(%{
+ "op" => "follow_import",
+ "user_id" => user.id,
+ "actor" => actor
+ })
+ |> Oban.insert()
+
+ acc ++ [job]
+ end)
+ end)
+
+ {:ok, jobs}
end
- def mutes_import(%User{} = user, [_ | _] = identifiers) do
- BackgroundWorker.new(%{
- "op" => "mutes_import",
- "user_id" => user.id,
- "identifiers" => identifiers
- })
- |> Oban.insert()
+ def mutes_import(%User{} = user, [_ | _] = actors) do
+ jobs =
+ Repo.checkout(fn ->
+ Enum.reduce(actors, [], fn actor, acc ->
+ {:ok, job} =
+ BackgroundWorker.new(%{
+ "op" => "mute_import",
+ "user_id" => user.id,
+ "actor" => actor
+ })
+ |> Oban.insert()
+
+ acc ++ [job]
+ end)
+ end)
+
+ {:ok, jobs}
end
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index cdd054e1a..a08eda5f4 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -311,7 +311,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
post_inbox_relayed_create(conn, params)
else
conn
- |> put_status(:bad_request)
+ |> put_status(403)
|> json("Not federating")
end
end
diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex
index 7f11a4d67..fc36935d5 100644
--- a/lib/pleroma/web/activity_pub/pipeline.ex
+++ b/lib/pleroma/web/activity_pub/pipeline.ex
@@ -22,22 +22,27 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
defp activity_pub, do: Config.get([:pipeline, :activity_pub], ActivityPub)
defp config, do: Config.get([:pipeline, :config], Config)
- @spec common_pipeline(map(), keyword()) ::
- {:ok, Activity.t() | Object.t(), keyword()} | {:error | :reject, any()}
+ @type results :: {:ok, Activity.t() | Object.t(), keyword()}
+ @type errors :: {:error | :reject, any()}
+
+ # The Repo.transaction will wrap the result in an {:ok, _}
+ # and only returns an {:error, _} if the error encountered was related
+ # to the SQL transaction
+ @spec common_pipeline(map(), keyword()) :: results() | errors()
def common_pipeline(object, meta) do
case Repo.transaction(fn -> do_common_pipeline(object, meta) end, Utils.query_timeout()) do
{:ok, {:ok, activity, meta}} ->
side_effects().handle_after_transaction(meta)
{:ok, activity, meta}
- {:ok, value} ->
- value
+ {:ok, {:error, _} = error} ->
+ error
+
+ {:ok, {:reject, _} = error} ->
+ error
{:error, e} ->
{:error, e}
-
- {:reject, e} ->
- {:reject, e}
end
end
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index 5cd982c6a..0de3a0d43 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -148,12 +148,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
{"digest", p.digest}
]
) do
- maybe_set_reachable(p.unreachable_since, p.inbox)
+ if not is_nil(p.unreachable_since) do
+ Instances.set_reachable(p.inbox)
+ end
result
else
{_post_result, %{status: code} = response} = e ->
- maybe_set_unreachable(p.unreachable_since, p.inbox)
+ if is_nil(p.unreachable_since) do
+ Instances.set_unreachable(p.inbox)
+ end
+
Logger.metadata(activity: p.activity_id, inbox: p.inbox, status: code)
Logger.error("Publisher failed to inbox #{p.inbox} with status #{code}")
@@ -174,7 +179,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
connection_pool_snooze()
e ->
- maybe_set_unreachable(p.unreachable_since, p.inbox)
+ if is_nil(p.unreachable_since) do
+ Instances.set_unreachable(p.inbox)
+ end
+
Logger.metadata(activity: p.activity_id, inbox: p.inbox)
Logger.error("Publisher failed to inbox #{p.inbox} #{inspect(e)}")
{:error, e}
@@ -183,12 +191,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
defp connection_pool_snooze, do: {:snooze, 3}
- defp maybe_set_reachable(%NaiveDateTime{}, inbox), do: Instances.set_reachable(inbox)
- defp maybe_set_reachable(_, _), do: :ok
-
- defp maybe_set_unreachable(nil, inbox), do: Instances.set_unreachable(inbox)
- defp maybe_set_unreachable(%NaiveDateTime{}, _), do: :ok
-
defp signature_host(%URI{port: port, scheme: scheme, host: host}) do
if port == URI.default_port(scheme) do
host
diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex
index 6e537b5da..25548d75b 100644
--- a/lib/pleroma/web/api_spec/schemas/status.ex
+++ b/lib/pleroma/web/api_spec/schemas/status.ex
@@ -249,6 +249,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
nullable: true,
description:
"A datetime (ISO 8601) that states when the post was pinned or `null` if the post is not pinned"
+ },
+ list_id: %Schema{
+ type: :integer,
+ nullable: true,
+ description:
+ "The ID of the list the post is addressed to (if any, only returned to author)"
}
}
},
diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex
index ea5620cf6..d31f34747 100644
--- a/lib/pleroma/web/auth/ldap_authenticator.ex
+++ b/lib/pleroma/web/auth/ldap_authenticator.ex
@@ -41,6 +41,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
port = Keyword.get(ldap, :port, 389)
ssl = Keyword.get(ldap, :ssl, false)
sslopts = Keyword.get(ldap, :sslopts, [])
+ tlsopts = Keyword.get(ldap, :tlsopts, [])
options =
[{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++
@@ -54,7 +55,16 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
case :eldap.start_tls(
connection,
- Keyword.get(ldap, :tlsopts, []),
+ Keyword.merge(
+ [
+ verify: :verify_peer,
+ cacerts: :certifi.cacerts(),
+ customize_hostname_check: [
+ fqdn_fun: fn _ -> to_charlist(host) end
+ ]
+ ],
+ tlsopts
+ ),
@connection_timeout
) do
:ok ->
diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex
index 921e414c3..412424dae 100644
--- a/lib/pleroma/web/common_api.ex
+++ b/lib/pleroma/web/common_api.ex
@@ -26,7 +26,7 @@ defmodule Pleroma.Web.CommonAPI do
require Pleroma.Constants
require Logger
- @spec block(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec block(User.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
def block(blocked, blocker) do
with {:ok, block_data, _} <- Builder.block(blocker, blocked),
{:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do
@@ -35,7 +35,7 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec post_chat_message(User.t(), User.t(), String.t(), list()) ::
- {:ok, Activity.t()} | {:error, any()}
+ {:ok, Activity.t()} | Pipeline.errors()
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
:ok <- validate_chat_attachment_attribution(maybe_attachment, user),
@@ -58,7 +58,7 @@ defmodule Pleroma.Web.CommonAPI do
)} do
{:ok, activity}
else
- {:common_pipeline, {:reject, _} = e} -> e
+ {:common_pipeline, e} -> e
e -> e
end
end
@@ -99,7 +99,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec unblock(User.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec unblock(User.t(), User.t()) ::
+ {:ok, Activity.t()} | {:ok, :no_activity} | Pipeline.errors() | {:error, :not_blocking}
def unblock(blocked, blocker) do
with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)},
{:ok, unblock_data, _} <- Builder.undo(blocker, block),
@@ -120,7 +121,9 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec follow(User.t(), User.t()) ::
- {:ok, User.t(), User.t(), Activity.t() | Object.t()} | {:error, :rejected}
+ {:ok, User.t(), User.t(), Activity.t() | Object.t()}
+ | {:error, :rejected}
+ | Pipeline.errors()
def follow(followed, follower) do
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
@@ -145,7 +148,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec accept_follow_request(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()}
+ @spec accept_follow_request(User.t(), User.t()) :: {:ok, User.t()} | Pipeline.errors()
def accept_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, accept_data, _} <- Builder.accept(followed, follow_activity),
@@ -154,7 +157,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec reject_follow_request(User.t(), User.t()) :: {:ok, User.t()} | {:error, any()} | nil
+ @spec reject_follow_request(User.t(), User.t()) :: {:ok, User.t()} | Pipeline.errors() | nil
def reject_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, reject_data, _} <- Builder.reject(followed, follow_activity),
@@ -163,7 +166,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec delete(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec delete(String.t(), User.t()) ::
+ {:ok, Activity.t()} | Pipeline.errors() | {:error, :not_found | String.t()}
def delete(activity_id, user) do
with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(activity_id, filter: [])},
@@ -213,7 +217,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec repeat(String.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec repeat(String.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, :not_found}
def repeat(id, user, params \\ %{}) do
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
object = %Object{} <- Object.normalize(activity, fetch: false),
@@ -231,7 +235,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec unrepeat(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec unrepeat(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, :not_found | String.t()}
def unrepeat(id, user) do
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(id)},
@@ -247,7 +251,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec favorite(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec favorite(String.t(), User.t()) ::
+ {:ok, Activity.t()} | {:ok, :already_liked} | {:error, :not_found | String.t()}
def favorite(id, %User{} = user) do
case favorite_helper(user, id) do
{:ok, _} = res ->
@@ -285,7 +290,8 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec unfavorite(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec unfavorite(String.t(), User.t()) ::
+ {:ok, Activity.t()} | {:error, :not_found | String.t()}
def unfavorite(id, user) do
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(id)},
@@ -302,7 +308,7 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec react_with_emoji(String.t(), User.t(), String.t()) ::
- {:ok, Activity.t()} | {:error, any()}
+ {:ok, Activity.t()} | {:error, String.t()}
def react_with_emoji(id, user, emoji) do
with %Activity{} = activity <- Activity.get_by_id(id),
object <- Object.normalize(activity, fetch: false),
@@ -316,7 +322,7 @@ defmodule Pleroma.Web.CommonAPI do
end
@spec unreact_with_emoji(String.t(), User.t(), String.t()) ::
- {:ok, Activity.t()} | {:error, any()}
+ {:ok, Activity.t()} | {:error, String.t()}
def unreact_with_emoji(id, user, emoji) do
with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji),
{_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(reaction_activity)},
@@ -329,7 +335,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec vote(Object.t(), User.t(), list()) :: {:ok, list(), Object.t()} | {:error, any()}
+ @spec vote(Object.t(), User.t(), list()) :: {:ok, list(), Object.t()} | Pipeline.errors()
def vote(%Object{data: %{"type" => "Question"}} = object, %User{} = user, choices) do
with :ok <- validate_not_author(object, user),
:ok <- validate_existing_votes(user, object),
@@ -461,7 +467,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec update(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec update(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, nil}
def update(orig_activity, %User{} = user, changes) do
with orig_object <- Object.normalize(orig_activity),
{:ok, new_object} <- make_update_data(user, orig_object, changes),
@@ -497,7 +503,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()}
+ @spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
def pin(id, %User{} = user) do
with %Activity{} = activity <- create_activity_by_id(id),
true <- activity_belongs_to_actor(activity, user.ap_id),
@@ -537,7 +543,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec unpin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()}
+ @spec unpin(String.t(), User.t()) :: {:ok, Activity.t()} | Pipeline.errors()
def unpin(id, user) do
with %Activity{} = activity <- create_activity_by_id(id),
{:ok, unpin_data, _} <- Builder.unpin(user, activity.object),
@@ -552,7 +558,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
- @spec add_mute(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
+ @spec add_mute(Activity.t(), User.t(), map()) :: {:ok, Activity.t()} | {:error, String.t()}
def add_mute(activity, user, params \\ %{}) do
expires_in = Map.get(params, :expires_in, 0)
diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex
index 2df716556..58260afa8 100644
--- a/lib/pleroma/web/federator.ex
+++ b/lib/pleroma/web/federator.ex
@@ -102,7 +102,8 @@ defmodule Pleroma.Web.Federator do
# NOTE: we use the actor ID to do the containment, this is fine because an
# actor shouldn't be acting on objects outside their own AP server.
- with {_, {:ok, _user}} <- {:actor, User.get_or_fetch_by_ap_id(actor)},
+ with {_, {:ok, user}} <- {:actor, User.get_or_fetch_by_ap_id(actor)},
+ {:user_active, true} <- {:user_active, match?(true, user.is_active)},
nil <- Activity.normalize(params["id"]),
{_, :ok} <-
{:correct_origin?, Containment.contain_origin_from_id(actor, params)},
@@ -121,11 +122,6 @@ defmodule Pleroma.Web.Federator do
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
{:error, e}
- {:error, {:validate_object, _}} = e ->
- Logger.error("Incoming AP doc validation error: #{inspect(e)}")
- Logger.debug(Jason.encode!(params, pretty: true))
- e
-
e ->
# Just drop those for now
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)
diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
index 844673ae0..6cfeb712e 100644
--- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
@@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+ plug(Pleroma.Web.Plugs.RateLimiter, [name: :oauth_app_creation] when action == :create)
+
plug(:skip_auth when action in [:create, :verify_credentials])
plug(Pleroma.Web.ApiSpec.CastAndValidate)
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 0643b8f14..7de6745d4 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -92,14 +92,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
User.get_follow_state(reading_user, target)
end
- followed_by =
- if following_relationships do
- case FollowingRelationship.find(following_relationships, target, reading_user) do
- %{state: :follow_accept} -> true
- _ -> false
- end
- else
- User.following?(target, reading_user)
+ followed_by = FollowingRelationship.following?(target, reading_user)
+ following = FollowingRelationship.following?(reading_user, target)
+
+ requested =
+ cond do
+ following -> false
+ true -> match?(:follow_pending, follow_state)
end
subscribing =
@@ -114,7 +113,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
%{
id: to_string(target.id),
- following: follow_state == :follow_accept,
+ following: following,
followed_by: followed_by,
blocking:
UserRelationship.exists?(
@@ -150,7 +149,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
),
subscribing: subscribing,
notifying: subscribing,
- requested: follow_state == :follow_pending,
+ requested: requested,
domain_blocking: User.blocks_domain?(reading_user, target),
showing_reblogs:
not UserRelationship.exists?(
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 747638c53..3bf870c24 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -465,7 +465,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
parent_visible: visible_for_user?(reply_to, opts[:for]),
pinned_at: pinned_at,
quotes_count: object.data["quotesCount"] || 0,
- bookmark_folder: bookmark_folder
+ bookmark_folder: bookmark_folder,
+ list_id: get_list_id(object, client_posted_this_activity)
}
}
end
@@ -803,19 +804,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp build_application(_), do: nil
- # Workaround for Elixir issue #10771
- # Avoid applying URI.merge unless necessary
- # TODO: revert to always attempting URI.merge(image_url_data, page_url_data)
- # when Elixir 1.12 is the minimum supported version
- @spec build_image_url(struct() | nil, struct()) :: String.t() | nil
- defp build_image_url(
- %URI{scheme: image_scheme, host: image_host} = image_url_data,
- %URI{} = _page_url_data
- )
- when not is_nil(image_scheme) and not is_nil(image_host) do
- image_url_data |> to_string
- end
-
+ @spec build_image_url(URI.t(), URI.t()) :: String.t()
defp build_image_url(%URI{} = image_url_data, %URI{} = page_url_data) do
URI.merge(page_url_data, image_url_data) |> to_string
end
@@ -847,4 +836,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
nil
end
end
+
+ defp get_list_id(object, client_posted_this_activity) do
+ with true <- client_posted_this_activity,
+ %{data: %{"listMessage" => list_ap_id}} when is_binary(list_ap_id) <- object,
+ %{id: list_id} <- Pleroma.List.get_by_ap_id(list_ap_id) do
+ list_id
+ else
+ _ -> nil
+ end
+ end
end
diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex
index d1bf6dd18..7661c2566 100644
--- a/lib/pleroma/web/o_auth/app.ex
+++ b/lib/pleroma/web/o_auth/app.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.OAuth.App do
import Ecto.Query
alias Pleroma.Repo
alias Pleroma.User
+ alias Pleroma.Web.OAuth.Token
@type t :: %__MODULE__{}
@@ -155,4 +156,29 @@ defmodule Pleroma.Web.OAuth.App do
Map.put(acc, key, error)
end)
end
+
+ @spec maybe_update_owner(Token.t()) :: :ok
+ def maybe_update_owner(%Token{app_id: app_id, user_id: user_id}) when not is_nil(user_id) do
+ __MODULE__.update(app_id, %{user_id: user_id})
+
+ :ok
+ end
+
+ def maybe_update_owner(_), do: :ok
+
+ @spec remove_orphans(pos_integer()) :: :ok
+ def remove_orphans(limit \\ 100) do
+ fifteen_mins_ago = DateTime.add(DateTime.utc_now(), -900, :second)
+
+ Repo.transaction(fn ->
+ from(a in __MODULE__,
+ where: is_nil(a.user_id) and a.inserted_at < ^fifteen_mins_ago,
+ limit: ^limit
+ )
+ |> Repo.all()
+ |> Enum.each(&Repo.delete(&1))
+ end)
+
+ :ok
+ end
end
diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex
index 47b03215f..0b3de5481 100644
--- a/lib/pleroma/web/o_auth/o_auth_controller.ex
+++ b/lib/pleroma/web/o_auth/o_auth_controller.ex
@@ -318,6 +318,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
def after_token_exchange(%Plug.Conn{} = conn, %{token: token} = view_params) do
+ App.maybe_update_owner(token)
+
conn
|> AuthHelper.put_session_token(token.token)
|> json(OAuthView.render("token.json", view_params))
diff --git a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex
index 96466f192..d65c30dab 100644
--- a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex
@@ -38,8 +38,8 @@ defmodule Pleroma.Web.PleromaAPI.UserImportController do
|> Enum.map(&(&1 |> String.trim() |> String.trim_leading("@")))
|> Enum.reject(&(&1 == ""))
- User.Import.follow_import(follower, identifiers)
- json(conn, "job started")
+ User.Import.follows_import(follower, identifiers)
+ json(conn, "jobs started")
end
def blocks(
@@ -55,7 +55,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportController do
defp do_block(%{assigns: %{user: blocker}} = conn, list) do
User.Import.blocks_import(blocker, prepare_user_identifiers(list))
- json(conn, "job started")
+ json(conn, "jobs started")
end
def mutes(
@@ -71,7 +71,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportController do
defp do_mute(%{assigns: %{user: user}} = conn, list) do
User.Import.mutes_import(user, prepare_user_identifiers(list))
- json(conn, "job started")
+ json(conn, "jobs started")
end
defp prepare_user_identifiers(list) do
diff --git a/lib/pleroma/web/plugs/authentication_plug.ex b/lib/pleroma/web/plugs/authentication_plug.ex
index f912a1542..af7d7f45a 100644
--- a/lib/pleroma/web/plugs/authentication_plug.ex
+++ b/lib/pleroma/web/plugs/authentication_plug.ex
@@ -47,6 +47,11 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
Pleroma.Password.Pbkdf2.verify_pass(password, password_hash)
end
+ def checkpw(password, "$argon2" <> _ = password_hash) do
+ # Handle argon2 passwords for Akkoma migration
+ Argon2.verify_pass(password, password_hash)
+ end
+
def checkpw(_password, _password_hash) do
Logger.error("Password hash not recognized")
false
@@ -56,6 +61,10 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
do_update_password(user, password)
end
+ def maybe_update_password(%User{password_hash: "$argon2" <> _} = user, password) do
+ do_update_password(user, password)
+ end
+
def maybe_update_password(user, _), do: {:ok, user}
defp do_update_password(user, password) do
diff --git a/lib/pleroma/web/plugs/inbox_guard_plug.ex b/lib/pleroma/web/plugs/inbox_guard_plug.ex
new file mode 100644
index 000000000..0064cce76
--- /dev/null
+++ b/lib/pleroma/web/plugs/inbox_guard_plug.ex
@@ -0,0 +1,89 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.InboxGuardPlug do
+ import Plug.Conn
+ import Pleroma.Constants, only: [activity_types: 0, allowed_activity_types_from_strangers: 0]
+
+ alias Pleroma.Config
+ alias Pleroma.User
+
+ def init(options) do
+ options
+ end
+
+ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
+ with {_, true} <- {:federating, Config.get!([:instance, :federating])} do
+ conn
+ |> filter_activity_types()
+ else
+ {:federating, false} ->
+ conn
+ |> json(403, "Not federating")
+ |> halt()
+ end
+ end
+
+ def call(conn, _opts) do
+ with {_, true} <- {:federating, Config.get!([:instance, :federating])},
+ conn = filter_activity_types(conn),
+ {:known, true} <- {:known, known_actor?(conn)} do
+ conn
+ else
+ {:federating, false} ->
+ conn
+ |> json(403, "Not federating")
+ |> halt()
+
+ {:known, false} ->
+ conn
+ |> filter_from_strangers()
+ end
+ end
+
+ # Early rejection of unrecognized types
+ defp filter_activity_types(%{body_params: %{"type" => type}} = conn) do
+ with true <- type in activity_types() do
+ conn
+ else
+ _ ->
+ conn
+ |> json(400, "Invalid activity type")
+ |> halt()
+ end
+ end
+
+ # If signature failed but we know this actor we should
+ # accept it as we may only need to refetch their public key
+ # during processing
+ defp known_actor?(%{body_params: data}) do
+ case Pleroma.Object.Containment.get_actor(data) |> User.get_cached_by_ap_id() do
+ %User{} -> true
+ _ -> false
+ end
+ end
+
+ # Only permit a subset of activity types from strangers
+ # or else it will add actors you've never interacted with
+ # to the database
+ defp filter_from_strangers(%{body_params: %{"type" => type}} = conn) do
+ with true <- type in allowed_activity_types_from_strangers() do
+ conn
+ else
+ _ ->
+ conn
+ |> json(400, "Invalid activity type for an unknown actor")
+ |> halt()
+ end
+ end
+
+ defp json(conn, status, resp) do
+ json_resp = Jason.encode!(resp)
+
+ conn
+ |> put_resp_content_type("application/json")
+ |> resp(status, json_resp)
+ |> halt()
+ end
+end
diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex
index e2889b351..d4be97957 100644
--- a/lib/pleroma/web/rich_media/helpers.ex
+++ b/lib/pleroma/web/rich_media/helpers.ex
@@ -11,16 +11,39 @@ defmodule Pleroma.Web.RichMedia.Helpers do
@spec rich_media_get(String.t()) :: {:ok, String.t()} | get_errors()
def rich_media_get(url) do
- headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
+ case Pleroma.HTTP.AdapterHelper.can_stream?() do
+ true -> stream(url)
+ false -> head_first(url)
+ end
+ |> handle_result(url)
+ end
+
+ defp stream(url) do
+ with {_, {:ok, %Tesla.Env{status: 200, body: stream_body, headers: headers}}} <-
+ {:get, Pleroma.HTTP.get(url, req_headers(), http_options())},
+ {_, :ok} <- {:content_type, check_content_type(headers)},
+ {_, :ok} <- {:content_length, check_content_length(headers)},
+ {:read_stream, {:ok, body}} <- {:read_stream, read_stream(stream_body)} do
+ {:ok, body}
+ end
+ end
+ defp head_first(url) do
with {_, {:ok, %Tesla.Env{status: 200, headers: headers}}} <-
- {:head, Pleroma.HTTP.head(url, headers, http_options())},
+ {:head, Pleroma.HTTP.head(url, req_headers(), http_options())},
{_, :ok} <- {:content_type, check_content_type(headers)},
{_, :ok} <- {:content_length, check_content_length(headers)},
{_, {:ok, %Tesla.Env{status: 200, body: body}}} <-
- {:get, Pleroma.HTTP.get(url, headers, http_options())} do
+ {:get, Pleroma.HTTP.get(url, req_headers(), http_options())} do
{:ok, body}
- else
+ end
+ end
+
+ defp handle_result(result, url) do
+ case result do
+ {:ok, body} ->
+ {:ok, body}
+
{:head, _} ->
Logger.debug("Rich media error for #{url}: HTTP HEAD failed")
{:error, :head}
@@ -29,8 +52,12 @@ defmodule Pleroma.Web.RichMedia.Helpers do
Logger.debug("Rich media error for #{url}: content-type is #{type}")
{:error, :content_type}
- {:content_length, {_, length}} ->
- Logger.debug("Rich media error for #{url}: content-length is #{length}")
+ {:content_length, :error} ->
+ Logger.debug("Rich media error for #{url}: content-length exceeded")
+ {:error, :body_too_large}
+
+ {:read_stream, :error} ->
+ Logger.debug("Rich media error for #{url}: content-length exceeded")
{:error, :body_too_large}
{:get, _} ->
@@ -59,7 +86,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
{_, maybe_content_length} ->
case Integer.parse(maybe_content_length) do
{content_length, ""} when content_length <= max_body -> :ok
- {_, ""} -> {:error, maybe_content_length}
+ {_, ""} -> :error
_ -> :ok
end
@@ -68,13 +95,37 @@ defmodule Pleroma.Web.RichMedia.Helpers do
end
end
- defp http_options do
- timeout = Config.get!([:rich_media, :timeout])
+ defp read_stream(stream) do
+ max_body = Keyword.get(http_options(), :max_body)
+
+ try do
+ result =
+ Stream.transform(stream, 0, fn chunk, total_bytes ->
+ new_total = total_bytes + byte_size(chunk)
+
+ if new_total > max_body do
+ raise("Exceeds max body limit of #{max_body}")
+ else
+ {[chunk], new_total}
+ end
+ end)
+ |> Enum.into(<<>>)
+ {:ok, result}
+ rescue
+ _ -> :error
+ end
+ end
+
+ defp http_options do
[
pool: :rich_media,
max_body: Config.get([:rich_media, :max_body], 5_000_000),
- tesla_middleware: [{Tesla.Middleware.Timeout, timeout: timeout}]
+ stream: true
]
end
+
+ defp req_headers do
+ [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
+ end
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 6492e3861..0423ca9e2 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -189,7 +189,7 @@ defmodule Pleroma.Web.Router do
end
pipeline :well_known do
- plug(:accepts, ["json", "jrd", "jrd+json", "xml", "xrd+xml"])
+ plug(:accepts, ["activity+json", "json", "jrd", "jrd+json", "xml", "xrd+xml"])
end
pipeline :config do
@@ -217,6 +217,10 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
end
+ pipeline :inbox_guard do
+ plug(Pleroma.Web.Plugs.InboxGuardPlug)
+ end
+
pipeline :static_fe do
plug(Pleroma.Web.Plugs.StaticFEPlug)
end
@@ -920,7 +924,7 @@ defmodule Pleroma.Web.Router do
end
scope "/", Pleroma.Web.ActivityPub do
- pipe_through(:activitypub)
+ pipe_through([:activitypub, :inbox_guard])
post("/inbox", ActivityPubController, :inbox)
post("/users/:nickname/inbox", ActivityPubController, :inbox)
end
diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex
index 60da2d5ca..4737c6ea2 100644
--- a/lib/pleroma/workers/background_worker.ex
+++ b/lib/pleroma/workers/background_worker.ex
@@ -19,10 +19,10 @@ defmodule Pleroma.Workers.BackgroundWorker do
User.perform(:force_password_reset, user)
end
- def perform(%Job{args: %{"op" => op, "user_id" => user_id, "identifiers" => identifiers}})
- when op in ["blocks_import", "follow_import", "mutes_import"] do
+ def perform(%Job{args: %{"op" => op, "user_id" => user_id, "actor" => actor}})
+ when op in ["block_import", "follow_import", "mute_import"] do
user = User.get_cached_by_id(user_id)
- {:ok, User.Import.perform(String.to_existing_atom(op), user, identifiers)}
+ User.Import.perform(String.to_existing_atom(op), user, actor)
end
def perform(%Job{
diff --git a/lib/pleroma/workers/cron/app_cleanup_worker.ex b/lib/pleroma/workers/cron/app_cleanup_worker.ex
new file mode 100644
index 000000000..ee71cd7b6
--- /dev/null
+++ b/lib/pleroma/workers/cron/app_cleanup_worker.ex
@@ -0,0 +1,21 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.Cron.AppCleanupWorker do
+ @moduledoc """
+ Cleans up registered apps that were never associated with a user.
+ """
+
+ use Oban.Worker, queue: "background"
+
+ alias Pleroma.Web.OAuth.App
+
+ @impl true
+ def perform(_job) do
+ App.remove_orphans()
+ end
+
+ @impl true
+ def timeout(_job), do: :timer.seconds(30)
+end
diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex
index d4db97b63..11b672bef 100644
--- a/lib/pleroma/workers/receiver_worker.ex
+++ b/lib/pleroma/workers/receiver_worker.ex
@@ -7,7 +7,7 @@ defmodule Pleroma.Workers.ReceiverWorker do
alias Pleroma.User
alias Pleroma.Web.Federator
- use Oban.Worker, queue: :federator_incoming, max_attempts: 5
+ use Oban.Worker, queue: :federator_incoming, max_attempts: 5, unique: [period: :infinity]
@impl true
@@ -33,7 +33,7 @@ defmodule Pleroma.Workers.ReceiverWorker do
query_string: query_string
}
- with {:ok, %User{} = _actor} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]),
+ with {:ok, %User{}} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]),
{:ok, _public_key} <- Signature.refetch_public_key(conn_data),
{:signature, true} <- {:signature, Signature.validate_signature(conn_data)},
{:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
@@ -56,17 +56,29 @@ defmodule Pleroma.Workers.ReceiverWorker do
def timeout(_job), do: :timer.seconds(5)
+ defp process_errors({:error, {:error, _} = error}), do: process_errors(error)
+
defp process_errors(errors) do
case errors do
- {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
+ # User fetch failures
+ {:error, :not_found} = reason -> {:cancel, reason}
+ {:error, :forbidden} = reason -> {:cancel, reason}
+ # Inactive user
+ {:error, {:user_active, false} = reason} -> {:cancel, reason}
+ # Validator will error and return a changeset error
+ # e.g., duplicate activities or if the object was deleted
+ {:error, {:validate, {:error, _changeset} = reason}} -> {:cancel, reason}
+ # Duplicate detection during Normalization
{:error, :already_present} -> {:cancel, :already_present}
- {:error, {:validate_object, _} = reason} -> {:cancel, reason}
- {:error, {:error, {:validate, {:error, _changeset} = reason}}} -> {:cancel, reason}
+ # MRFs will return a reject
{:error, {:reject, _} = reason} -> {:cancel, reason}
+ # HTTP Sigs
{:signature, false} -> {:cancel, :invalid_signature}
- {:error, "Object has been deleted"} = reason -> {:cancel, reason}
+ # Origin / URL validation failed somewhere possibly due to spoofing
+ {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
+ # Unclear if this can be reached
{:error, {:side_effects, {:error, :no_object_actor}} = reason} -> {:cancel, reason}
- {:error, :not_found} = reason -> {:cancel, reason}
+ # Catchall
{:error, _} = e -> e
e -> {:error, e}
end
diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex
index e43765733..aa09362f5 100644
--- a/lib/pleroma/workers/remote_fetcher_worker.ex
+++ b/lib/pleroma/workers/remote_fetcher_worker.ex
@@ -5,7 +5,7 @@
defmodule Pleroma.Workers.RemoteFetcherWorker do
alias Pleroma.Object.Fetcher
- use Oban.Worker, queue: :background
+ use Oban.Worker, queue: :background, unique: [period: :infinity]
@impl true
def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do
@@ -13,17 +13,26 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do
{:ok, _object} ->
:ok
- {:reject, reason} ->
+ {:allowed_depth, false} ->
+ {:cancel, :allowed_depth}
+
+ {:containment, reason} ->
{:cancel, reason}
- {:error, :forbidden} ->
- {:cancel, :forbidden}
+ {:transmogrifier, reason} ->
+ {:cancel, reason}
- {:error, :not_found} ->
- {:cancel, :not_found}
+ {:fetch, {:error, :forbidden = reason}} ->
+ {:cancel, reason}
- {:error, :allowed_depth} ->
- {:cancel, :allowed_depth}
+ {:fetch, {:error, :not_found = reason}} ->
+ {:cancel, reason}
+
+ {:fetch, {:error, {:content_type, _}} = reason} ->
+ {:cancel, reason}
+
+ {:fetch, {:error, reason}} ->
+ {:error, reason}
{:error, _} = e ->
e
diff --git a/lib/pleroma/workers/rich_media_worker.ex b/lib/pleroma/workers/rich_media_worker.ex
index d5ba7b63e..e351ecd6e 100644
--- a/lib/pleroma/workers/rich_media_worker.ex
+++ b/lib/pleroma/workers/rich_media_worker.ex
@@ -7,7 +7,7 @@ defmodule Pleroma.Workers.RichMediaWorker do
alias Pleroma.Web.RichMedia.Backfill
alias Pleroma.Web.RichMedia.Card
- use Oban.Worker, queue: :background, max_attempts: 3, unique: [period: 300]
+ use Oban.Worker, queue: :background, max_attempts: 3, unique: [period: :infinity]
@impl true
def perform(%Job{args: %{"op" => "expire", "url" => url} = _args}) do
diff --git a/lib/pleroma/workers/user_refresh_worker.ex b/lib/pleroma/workers/user_refresh_worker.ex
index 222a4a8f7..ee276774b 100644
--- a/lib/pleroma/workers/user_refresh_worker.ex
+++ b/lib/pleroma/workers/user_refresh_worker.ex
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.UserRefreshWorker do
- use Oban.Worker, queue: :background, max_attempts: 1, unique: [period: 300]
+ use Oban.Worker, queue: :background, max_attempts: 1, unique: [period: :infinity]
alias Pleroma.User
diff --git a/lib/pleroma/workers/web_pusher_worker.ex b/lib/pleroma/workers/web_pusher_worker.ex
index f4232d02a..879b26cc3 100644
--- a/lib/pleroma/workers/web_pusher_worker.ex
+++ b/lib/pleroma/workers/web_pusher_worker.ex
@@ -7,7 +7,7 @@ defmodule Pleroma.Workers.WebPusherWorker do
alias Pleroma.Repo
alias Pleroma.Web.Push.Impl
- use Oban.Worker, queue: :web_push
+ use Oban.Worker, queue: :web_push, unique: [period: :infinity]
@impl true
def perform(%Job{args: %{"op" => "web_push", "notification_id" => notification_id}}) do
diff --git a/mix.exs b/mix.exs
index e3c8559ba..9a261547f 100644
--- a/mix.exs
+++ b/mix.exs
@@ -144,7 +144,7 @@ defmodule Pleroma.Mixfile do
{:telemetry_poller, "~> 1.0"},
{:tzdata, "~> 1.0.3"},
{:plug_cowboy, "~> 2.5"},
- {:oban, "~> 2.17.9"},
+ {:oban, "~> 2.18.0"},
{:gettext, "~> 0.20"},
{:bcrypt_elixir, "~> 2.2"},
{:trailing_format_plug, "~> 0.0.7"},
@@ -158,7 +158,7 @@ defmodule Pleroma.Mixfile do
{:gun, "~> 2.0.0-rc.1", override: true},
{:finch, "~> 0.15"},
{:jason, "~> 1.2"},
- {:mogrify, "~> 0.8.0"},
+ {:mogrify, "~> 0.9.0", override: "true"},
{:ex_aws, "~> 2.1.6"},
{:ex_aws_s3, "~> 2.0"},
{:sweet_xml, "~> 0.7.2"},
@@ -203,6 +203,8 @@ defmodule Pleroma.Mixfile do
{:websock_adapter, "~> 0.5.6"},
{:oban_live_dashboard, "~> 0.1.1"},
{:multipart, "~> 0.4.0", optional: true},
+ {:argon2_elixir, "~> 4.0"},
+ {:certifi, "~> 2.12"},
## dev & test
{:phoenix_live_reload, "~> 1.3.3", only: :dev},
diff --git a/mix.lock b/mix.lock
index 37ac1768b..01f2eef98 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,5 +1,6 @@
%{
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
+ "argon2_elixir": {:hex, :argon2_elixir, "4.0.0", "7f6cd2e4a93a37f61d58a367d82f830ad9527082ff3c820b8197a8a736648941", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f9da27cf060c9ea61b1bd47837a28d7e48a8f6fa13a745e252556c14f9132c7f"},
"bandit": {:hex, :bandit, "1.5.5", "df28f1c41f745401fe9e85a6882033f5f3442ab6d30c8a2948554062a4ab56e0", [:mix], [{:hpax, "~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f21579a29ea4bc08440343b2b5f16f7cddf2fea5725d31b72cf973ec729079e1"},
"base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
@@ -22,7 +23,7 @@
"cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
- "credo": {:hex, :credo, "1.7.3", "05bb11eaf2f2b8db370ecaa6a6bda2ec49b2acd5e0418bc106b73b07128c0436", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "35ea675a094c934c22fb1dca3696f3c31f2728ae6ef5a53b5d648c11180a4535"},
+ "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
@@ -65,7 +66,7 @@
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"inet_cidr": {:hex, :inet_cidr, "1.0.8", "d26bb7bdbdf21ae401ead2092bf2bb4bf57fe44a62f5eaa5025280720ace8a40", [:mix], [], "hexpm", "d5b26da66603bb56c933c65214c72152f0de9a6ea53618b56d63302a68f6a90e"},
- "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"},
+ "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"},
"jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"},
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
@@ -82,14 +83,14 @@
"mint": {:hex, :mint, "1.6.1", "065e8a5bc9bbd46a41099dfea3e0656436c5cbcb6e741c80bd2bad5cd872446f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4fc518dcc191d02f433393a72a7ba3f6f94b101d094cb6bf532ea54c89423780"},
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
"mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"},
- "mogrify": {:hex, :mogrify, "0.8.0", "3506f3ca3f7b95a155f3b4ef803b5db176f5a0633723e3fe85e0d6399e3b11c8", [:mix], [], "hexpm", "2278d245f07056ea3b586e98801e933695147066fa4cf563f552c1b4f0ff8ad9"},
+ "mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
"mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"},
"multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"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.17.12", "33fb0cbfb92b910d48dd91a908590fe3698bb85eacec8cd0d9bc6aa13dddd6d6", [: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", "7a647d6cd6bb300073db17faabce22d80ae135da3baf3180a064fa7c4fa046e3"},
+ "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_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/priv/gettext/zh_Hans/LC_MESSAGES/config_descriptions.po b/priv/gettext/zh_Hans/LC_MESSAGES/config_descriptions.po
index ff9ad5245..a56c90724 100644
--- a/priv/gettext/zh_Hans/LC_MESSAGES/config_descriptions.po
+++ b/priv/gettext/zh_Hans/LC_MESSAGES/config_descriptions.po
@@ -3,9 +3,9 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-07-21 04:21+0300\n"
-"PO-Revision-Date: 2022-07-24 10:04+0000\n"
-"Last-Translator: Yating Zhan <thestrandedvalley@protonmail.com>\n"
-"Language-Team: Chinese (Simplified) <http://weblate.pleroma-dev.ebin.club/"
+"PO-Revision-Date: 2024-08-02 09:02+0000\n"
+"Last-Translator: Eric Zhang <ericzhang456@disroot.org>\n"
+"Language-Team: Chinese (Simplified) <https://translate.pleroma.social/"
"projects/pleroma/pleroma-backend-domain-config_descriptions/zh_Hans/>\n"
"Language: zh_Hans\n"
"MIME-Version: 1.0\n"
@@ -49,6 +49,8 @@ msgstr "Mime 类型设置"
msgctxt "config description at :pleroma"
msgid "Allows setting a token that can be used to authenticate requests with admin privileges without a normal user account token. Append the `admin_token` parameter to requests to utilize it. (Please reconsider using HTTP Basic Auth or OAuth-based authentication if possible)"
msgstr ""
+"允许设置令牌以不使用普通用户令牌来授权管理员权限。在参数后加上 `admin_token` "
+"来启用该功能。(可用时可以考虑使用 HTTP Basic Auth 或 基于 OAuth 的鉴定方式)"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -126,7 +128,7 @@ msgstr "ActivityPub 相关设置"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:assets"
msgid "This section configures assets to be used with various frontends. Currently the only option relates to mascots on the mastodon frontend"
-msgstr ""
+msgstr "该部分配置不同前端使用的资源。目前该选项只对 Mastodon 前端的吉祥物有效"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -138,7 +140,7 @@ msgstr "鉴权/授权设置"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:connections_pool"
msgid "Advanced settings for `Gun` connections pool"
-msgstr "「Gun」连接池的高级设置"
+msgstr "`Gun` 连接池的高级设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -183,7 +185,7 @@ msgstr "Gopher 设置"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:hackney_pools"
msgid "Advanced settings for `Hackney` connections pools"
-msgstr "「Hackney」连接池的高级设置"
+msgstr "`Hackney` 连接池的高级设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -226,7 +228,7 @@ msgid "Majic/libmagic configuration"
msgstr "Majic/libmagic 配置"
#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
+#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:manifest"
msgid "This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE."
msgstr "此处提供针对特定实例的 PWA manifest 数值。目前相关设定尚只支持 MastoFE。"
@@ -244,10 +246,10 @@ msgid "Media proxy"
msgstr "媒体代理"
#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
+#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:modules"
msgid "Custom Runtime Modules"
-msgstr "自定义 Runtime 模块"
+msgstr "自定义运行库模块"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -299,7 +301,7 @@ msgstr "拒绝提及特定用户的讯息"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:mrf_normalize_markup"
msgid "MRF NormalizeMarkup settings. Scrub configured hypertext markup."
-msgstr ""
+msgstr "MRF NomalizeMarkup 设置。清楚超文本标记。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -317,7 +319,7 @@ msgstr "RejectNonPublic 丢弃有非公开的可见性设置的文章。"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:mrf_simple"
msgid "Simple ingress policies"
-msgstr ""
+msgstr "简单入口流量控制"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -326,10 +328,11 @@ msgid "Steals emojis from selected instances when it sees them."
msgstr "从选择的实例偷取看到的 emoji。"
#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format
+#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:mrf_subchain"
msgid "This policy processes messages through an alternate pipeline when a given message matches certain criteria. All criteria are configured as a map of regular expressions to lists of policy modules."
-msgstr ""
+msgstr "此策略将会把满足特定条件的信息通过另一管线处理。所有条件都以正则表达式来对应"
+"列出的策略模块配置。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -347,13 +350,13 @@ msgstr "配置 OAuth 2 提供者的能力"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:pools"
msgid "Advanced settings for `Gun` workers pools"
-msgstr "「Gun」工人池的高级设置"
+msgstr "`Gun` worker 池的高级设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:populate_hashtags_table"
msgid "`populate_hashtags_table` background migration settings"
-msgstr "「populate_hashtags_table」后台迁移设置"
+msgstr "`populate_hashtags_table` 后台迁移设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -395,13 +398,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:uri_schemes"
msgid "URI schemes related settings"
-msgstr ""
+msgstr "URI scheme 相关设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:web_cache_ttl"
msgid "The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration."
-msgstr "web 回应缓存的过期时间。值应该以毫秒为单位,或者用「nil」来禁用过期。"
+msgstr "网页回应缓存的过期时间。以毫秒为单位,或者用 `nil` 来禁用过期。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -413,7 +416,7 @@ msgstr "欢迎讯息设置"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:workers"
msgid "Includes custom worker options not interpretable directly by `Oban`"
-msgstr "包含不能直接被「Oban」解读的自定工人选项"
+msgstr "包含不能直接被 `Oban` 解读的自定 worker 选项"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -488,7 +491,7 @@ msgstr "过滤器将会匿名化上传文件的文件名"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-Pleroma.Upload.Filter.Mogrify"
msgid "Uploads mogrify filter settings"
-msgstr ""
+msgstr "morgify 上传过滤器设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -531,6 +534,9 @@ msgstr "元数据相关设定"
msgctxt "config description at :pleroma-Pleroma.Web.Plugs.RemoteIp"
msgid "`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.\n**If your instance is not behind at least one reverse proxy, you should not enable this plug.**\n"
msgstr ""
+"`Pleroma.Web.Plugs.RemoteIp` 是一个呼叫 [`RemoteIp`](https://git.pleroma."
+"social/pleroma/remote_ip) 的 shim 但是包含运行库配置。\n"
+"**如果您的实例不在至少一个反向代理后面,您不应该启用这个插件。**\n"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -548,13 +554,13 @@ msgstr "失效活动设定"
#, elixir-autogen, elixir-format
msgctxt "config description at :prometheus-Pleroma.Web.Endpoint.MetricsExporter"
msgid "Prometheus app metrics endpoint configuration"
-msgstr ""
+msgstr "Prometheus 服务监控端点配置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :web_push_encryption-:vapid_details"
msgid "Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it."
-msgstr ""
+msgstr "网页推送通知配置。您可以使用 mix task mix web_push.gen.keypair 来生成它。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -584,7 +590,7 @@ msgstr "ActivityPub"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:assets"
msgid "Assets"
-msgstr ""
+msgstr "资源"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -608,13 +614,13 @@ msgstr "邮件通知"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:emoji"
msgid "Emoji"
-msgstr "Emoji"
+msgstr "表情符号"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:features"
msgid "Features"
-msgstr "特性"
+msgstr "功能"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -644,7 +650,7 @@ msgstr "Gopher"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:hackney_pools"
msgid "Hackney pools"
-msgstr ""
+msgstr "Hackney 池"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -674,25 +680,25 @@ msgstr "实例图标"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:ldap"
msgid "LDAP"
-msgstr ""
+msgstr "LDAP"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:majic_pool"
msgid "Majic pool"
-msgstr ""
+msgstr "Majic 池"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:manifest"
msgid "Manifest"
-msgstr ""
+msgstr "Manifest"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:markup"
msgid "Markup Settings"
-msgstr ""
+msgstr "标记设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -710,25 +716,25 @@ msgstr "媒体文件代理"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:modules"
msgid "Modules"
-msgstr ""
+msgstr "模块"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf"
msgid "MRF"
-msgstr ""
+msgstr "MRF"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_activity_expiration"
msgid "MRF Activity Expiration Policy"
-msgstr ""
+msgstr "MRF 活动过期策略"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_follow_bot"
msgid "MRF FollowBot Policy"
-msgstr ""
+msgstr "MRF FollowBot 策略"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -740,49 +746,49 @@ msgstr "MRF 标签"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_hellthread"
msgid "MRF Hellthread"
-msgstr ""
+msgstr "MRF Hellthread"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_keyword"
msgid "MRF Keyword"
-msgstr ""
+msgstr "MRF 关键词"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_mention"
msgid "MRF Mention"
-msgstr ""
+msgstr "MRF 提及"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_normalize_markup"
msgid "MRF Normalize Markup"
-msgstr ""
+msgstr "MRF 标记标准化"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_object_age"
msgid "MRF Object Age"
-msgstr ""
+msgstr "MRF 对象年龄"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_rejectnonpublic"
msgid "MRF Reject Non Public"
-msgstr ""
+msgstr "MRF 拒绝非公开帖子"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_simple"
msgid "MRF Simple"
-msgstr ""
+msgstr "MRF 简单"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:mrf_steal_emoji"
msgid "MRF Emojis"
-msgstr ""
+msgstr "MRF 表情符号"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -800,13 +806,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:oauth2"
msgid "OAuth2"
-msgstr ""
+msgstr "OAuth2"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:pools"
msgid "Pools"
-msgstr ""
+msgstr "池"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -818,13 +824,13 @@ msgstr "本站话题标签列表"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:rate_limit"
msgid "Rate limit"
-msgstr ""
+msgstr "限流"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:restrict_unauthenticated"
msgid "Restrict Unauthenticated"
-msgstr ""
+msgstr "限制未授权用户"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -842,7 +848,7 @@ msgstr "留言板"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:static_fe"
msgid "Static FE"
-msgstr ""
+msgstr "静态 FE"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -854,7 +860,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:uri_schemes"
msgid "URI Schemes"
-msgstr ""
+msgstr "URI Schemes"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -866,7 +872,7 @@ msgstr "用户"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:web_cache_ttl"
msgid "Web cache TTL"
-msgstr ""
+msgstr "网页缓存 TTL"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -878,13 +884,13 @@ msgstr "欢迎"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:workers"
msgid "Workers"
-msgstr "工人"
+msgstr "Workers"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-ConcurrentLimiter"
msgid "ConcurrentLimiter"
-msgstr ""
+msgstr "ConcurrentLimiter"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -896,133 +902,133 @@ msgstr "Oban"
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Captcha"
msgid "Pleroma.Captcha"
-msgstr ""
+msgstr "Pleroma.Captcha"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Captcha.Kocaptcha"
msgid "Pleroma.Captcha.Kocaptcha"
-msgstr ""
+msgstr "Pleroma.Captcha.Kocaptcha"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Emails.Mailer"
msgid "Pleroma.Emails.Mailer"
-msgstr ""
+msgstr "Pleroma.Emails.Mailer"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Emails.NewUsersDigestEmail"
msgid "Pleroma.Emails.NewUsersDigestEmail"
-msgstr ""
+msgstr "Pleroma.Emails.NewUsersDigestEmail"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Emails.UserEmail"
msgid "Pleroma.Emails.UserEmail"
-msgstr ""
+msgstr "Pleroma.Emails.UserEmail"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Formatter"
msgid "Linkify"
-msgstr ""
+msgstr "Linkify"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.ScheduledActivity"
msgid "Pleroma.ScheduledActivity"
-msgstr ""
+msgstr "Pleroma.ScheduledActivity"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Upload"
msgid "Pleroma.Upload"
-msgstr ""
+msgstr "Pleroma.Upload"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Upload.Filter.AnonymizeFilename"
msgid "Pleroma.Upload.Filter.AnonymizeFilename"
-msgstr ""
+msgstr "Pleroma.Upload.Filter.AnonymizeFilename"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Upload.Filter.Mogrify"
msgid "Pleroma.Upload.Filter.Mogrify"
-msgstr ""
+msgstr "Pleroma.Upload.Filter.Mogrify"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Uploaders.Local"
msgid "Pleroma.Uploaders.Local"
-msgstr ""
+msgstr "Pleroma.Uploaders.Local"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Uploaders.S3"
msgid "Pleroma.Uploaders.S3"
-msgstr ""
+msgstr "Pleroma.Uploaders.S3"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.User"
msgid "Pleroma.User"
-msgstr ""
+msgstr "Pleroma.User"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.User.Backup"
msgid "Pleroma.User.Backup"
-msgstr ""
+msgstr "Pleroma.User.Backup"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Web.ApiSpec.CastAndValidate"
msgid "Pleroma.Web.ApiSpec.CastAndValidate"
-msgstr ""
+msgstr "Pleroma.Web.ApiSpec.CastAndValidate"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Web.MediaProxy.Invalidation.Http"
msgid "Pleroma.Web.MediaProxy.Invalidation.Http"
-msgstr ""
+msgstr "Pleroma.Web.MediaProxy.Invalidation.Http"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Web.MediaProxy.Invalidation.Script"
msgid "Pleroma.Web.MediaProxy.Invalidation.Script"
-msgstr ""
+msgstr "Pleroma.Web.MediaProxy.Invalidation.Script"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Web.Metadata"
msgid "Pleroma.Web.Metadata"
-msgstr ""
+msgstr "Pleroma.Web.Metadata"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Web.Plugs.RemoteIp"
msgid "Pleroma.Web.Plugs.RemoteIp"
-msgstr ""
+msgstr "Pleroma.Web.Plugs.RemoteIp"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Web.Preload"
msgid "Pleroma.Web.Preload"
-msgstr ""
+msgstr "Pleroma.Web.Preload"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-Pleroma.Workers.PurgeExpiredActivity"
msgid "Pleroma.Workers.PurgeExpiredActivity"
-msgstr ""
+msgstr "Pleroma.Workers.PurgeExpiredActivity"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config label at :prometheus-Pleroma.Web.Endpoint.MetricsExporter"
msgid "Pleroma.Web.Endpoint.MetricsExporter"
-msgstr ""
+msgstr "Pleroma.Web.Endpoint.MetricsExporter"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1064,37 +1070,39 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :ex_aws-:s3 > :access_key_id"
msgid "S3 access key ID"
-msgstr ""
+msgstr "S3 访问密钥 ID"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :ex_aws-:s3 > :host"
msgid "S3 host"
-msgstr ""
+msgstr "S3 主机"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :ex_aws-:s3 > :region"
msgid "S3 region (for AWS)"
-msgstr ""
+msgstr "S3 区域(AWS)"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :ex_aws-:s3 > :secret_access_key"
msgid "Secret access key"
-msgstr ""
+msgstr "访问密钥"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :logger > :backends"
msgid "Where logs will be sent, :console - send logs to stdout, { ExSyslogger, :ex_syslogger } - to syslog, Quack.Logger - to Slack."
msgstr ""
+"日志发送的地点,:console - 将日志发送到 stdout, { ExSyslogger, :ex_syslogger "
+"} - 发送到 syslog, Quack.Logger - 发送到 Slack."
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :logger-:console > :format"
msgid "Default: \"$date $time [$level] $levelpad$node $metadata $message\""
-msgstr ""
+msgstr "默认:\"$date $time [$level] $levelpad$node $metadata $message\""
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1106,13 +1114,13 @@ msgstr "日志等级"
#, elixir-autogen, elixir-format
msgctxt "config description at :logger-:ex_syslogger > :format"
msgid "Default: \"$date $time [$level] $levelpad$node $metadata $message\""
-msgstr ""
+msgstr "默认:\"$date $time [$level] $levelpad$node $metadata $message\""
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :logger-:ex_syslogger > :ident"
msgid "A string that's prepended to every message, and is typically set to the app name"
-msgstr ""
+msgstr "注入在每一个消息前面的字符串,通常设为应用程序的名称"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1148,13 +1156,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:activitypub > :outgoing_blocks"
msgid "Whether to federate blocks to other instances"
-msgstr ""
+msgstr "是否与其他实例同步屏蔽列表"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:activitypub > :sign_object_fetches"
msgid "Sign object fetches with HTTP signatures"
-msgstr ""
+msgstr "为对象获取进行 HTTP 签名"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1166,7 +1174,7 @@ msgstr "屏蔽对象时是否同时取消对其的关注"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:assets > :default_mascot"
msgid "This will be used as the default mascot on MastoFE. Default: `:pleroma_fox_tan`"
-msgstr ""
+msgstr "这将是 MastoFE 的默认吉祥物。默认:`:pleroma_fox_tan`"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1178,13 +1186,15 @@ msgstr "默认用户头像的网址"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:assets > :mascots"
msgid "Keyword of mascots, each element must contain both an URL and a mime_type key"
-msgstr ""
+msgstr "吉祥物关键词,每一个元素必须包含一个 URL 和 mine_type 值"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:auth > :auth_template"
msgid "Authentication form template. By default it's `show.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/show.html.ee`."
msgstr ""
+"授权表达模板。默认是 `show.html`,对应于 `lib/pleroma/web/templates/o_auth/"
+"o_auth/show.html.ee`。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1208,7 +1218,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:connections_pool > :connect_timeout"
msgid "Timeout while `gun` will wait until connection is up. Default: 5000ms."
-msgstr "「Gun」等待连接时触发超时的上限。默认为5000ms。"
+msgstr "`gun` 等待连接时触发超时的上限。默认:5000ms。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1226,7 +1236,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:connections_pool > :max_connections"
msgid "Maximum number of connections in the pool. Default: 250 connections."
-msgstr ""
+msgstr "池的最大连接数量。默认:250 连接。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1262,13 +1272,15 @@ msgstr "单个用户每次收到摘要邮件的间隔"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:email_notifications > :digest > :schedule"
msgid "When to send digest email, in crontab format. \"0 0 0\" is the default, meaning \"once a week at midnight on Sunday morning\"."
-msgstr ""
+msgstr "发送摘要邮件的时间,以 crontab 格式。默认为“0 0 "
+"0”,意味着“每周在周日的午夜时分”。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:emoji > :default_manifest"
msgid "Location of the JSON-manifest. This manifest contains information about the emoji-packs you can download. Currently only one manifest can be added (no arrays)."
-msgstr ""
+msgstr "JSON-manifest 的位置。manifest 包含您可以下载的表情包信息。目前只能添加一个 "
+"manifest(无数列)。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1286,7 +1298,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:emoji > :shortcode_globs"
msgid "Location of custom emoji files. * can be used as a wildcard."
-msgstr ""
+msgstr "自定义表情符号位置。* 可以当作通配符使用。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1298,7 +1310,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:feed > :post_title"
msgid "Configure title rendering"
-msgstr ""
+msgstr "配置标题渲染"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1468,7 +1480,7 @@ msgstr "管理员前端"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:frontends > :admin > name"
msgid "Name of the installed frontend. Valid config must include both `Name` and `Reference` values."
-msgstr "已安装的前端名称。只有包含了「名称」与「引用」数值才能被算作有效配置。"
+msgstr "已安装的前端名称。有效配置必须包含 `Name` 和 `Reference` 数值。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1498,13 +1510,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:frontends > :available > custom-http-headers"
msgid "The custom HTTP headers for the frontend"
-msgstr ""
+msgstr "前端的自定义 HTTP 响应头"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:frontends > :available > git"
msgid "URL of the git repository of the frontend"
-msgstr ""
+msgstr "前端 git 仓库的 URL"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1522,13 +1534,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:frontends > :primary"
msgid "Primary frontend, the one that is served for all pages by default"
-msgstr ""
+msgstr "主要前端,这是默认服务所有页面的前端"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:frontends > :primary > name"
msgid "Name of the installed frontend. Valid config must include both `Name` and `Reference` values."
-msgstr "已安装的前端名称。只有包含了「名称」与「引用」数值才能被算作有效配置。"
+msgstr "已安装的前端名称。有效配置必须包含 `Name` 和 `Reference` 数值。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1540,7 +1552,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:gopher > :dstport"
msgid "Port advertised in URLs (optional, defaults to port)"
-msgstr ""
+msgstr "URL 中宣传的端口(可选,默认为端口)"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1570,7 +1582,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:hackney_pools > :federation > :max_connections"
msgid "Number workers in the pool."
-msgstr "池内的工人数量。"
+msgstr "池内的 worker 数量。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1588,7 +1600,7 @@ msgstr "媒体池设定。"
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:hackney_pools > :media > :max_connections"
msgid "Number workers in the pool."
-msgstr "池内的工人数量。"
+msgstr "池内的 worker 数量。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1600,7 +1612,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:hackney_pools > :upload"
msgid "Settings for upload pool."
-msgstr ""
+msgstr "上传池设置。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1780,7 +1792,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:instance > :email"
msgid "Email used to reach an Administrator/Moderator of the instance"
-msgstr ""
+msgstr "用于联系实例管理员/监管员的电子邮箱"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1813,10 +1825,10 @@ msgid "Timeout (in days) of each external federation target being unreachable pr
msgstr ""
#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
+#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:instance > :healthcheck"
msgid "If enabled, system data will be shown on `/api/pleroma/healthcheck`"
-msgstr "若启用,「/api/pleroma/healthcheck」下将显示系统数据"
+msgstr "若启用,`/api/pleroma/healthcheck` 下将显示系统数据"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1828,7 +1840,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:instance > :invites_enabled"
msgid "Enable user invitations for admins (depends on `registrations_open` being disabled)"
-msgstr "只有管理员邀请的用户方能注册(需要关闭「registrations_open」选项)"
+msgstr "只有管理员邀请的用户才能注册(需要关闭 `registrations_open` 选项)"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1900,13 +1912,13 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:instance > :multi_factor_authentication > :backup_codes > :number"
msgid "Number of backup codes to generate."
-msgstr ""
+msgstr "生成的备份密钥数目。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:instance > :multi_factor_authentication > :totp"
msgid "TOTP settings"
-msgstr ""
+msgstr "TOTP 设置"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -1973,7 +1985,7 @@ msgstr "允许管理员访问敏感信息(例,更新用户凭据、取得密
#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:instance > :profile_directory"
msgid "Enable profile directory."
-msgstr ""
+msgstr "启用用户主页配置。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -2020,10 +2032,10 @@ msgstr ""
"用户(例,“@admin 请留意 @bad_actor”)。默认下为关闭状态"
#: lib/pleroma/docs/translator.ex:5
-#, elixir-autogen, elixir-format, fuzzy
+#, elixir-autogen, elixir-format
msgctxt "config description at :pleroma-:instance > :show_reactions"
msgid "Let favourites and emoji reactions be viewed through the API."
-msgstr "允许通过此API来看见喜欢数量与表情反应。"
+msgstr "允许通过此 API 来看见喜欢数量与表情回应。"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
@@ -3607,7 +3619,7 @@ msgstr ""
#, elixir-autogen, elixir-format
msgctxt "config label at :pleroma-:assets > :mascots"
msgid "Mascots"
-msgstr ""
+msgstr "吉祥物"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format
diff --git a/priv/gettext/zh_Hans/LC_MESSAGES/default.po b/priv/gettext/zh_Hans/LC_MESSAGES/default.po
index ed0d1576b..7d5828fae 100644
--- a/priv/gettext/zh_Hans/LC_MESSAGES/default.po
+++ b/priv/gettext/zh_Hans/LC_MESSAGES/default.po
@@ -8,9 +8,9 @@
## to merge POT files into PO files.
msgid ""
msgstr ""
-"PO-Revision-Date: 2022-07-22 19:00+0000\n"
-"Last-Translator: Yating Zhan <thestrandedvalley@protonmail.com>\n"
-"Language-Team: Chinese (Simplified) <http://weblate.pleroma-dev.ebin.club/"
+"PO-Revision-Date: 2024-08-02 09:02+0000\n"
+"Last-Translator: Eric Zhang <ericzhang456@disroot.org>\n"
+"Language-Team: Chinese (Simplified) <https://translate.pleroma.social/"
"projects/pleroma/pleroma-backend-domain-default/zh_Hans/>\n"
"Language: zh_Hans\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -106,7 +106,7 @@ msgstr "转换到 %{polymorphic_type} 中的任一 schema 失败"
#: lib/pleroma/web/api_spec/render_error.ex:71
#, elixir-format
msgid "Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed."
-msgstr "把值转换成 %{invalid_schema} 失败。值必须可以被转换成在列的「所有」schema。"
+msgstr "把值转换成 %{invalid_schema} 失败。值必须可以被转换成在列的 `allOf` schema。"
#: lib/pleroma/web/api_spec/render_error.ex:84
#, elixir-format
@@ -136,17 +136,17 @@ msgstr "缺少头:%{name}。"
#: lib/pleroma/web/api_spec/render_error.ex:196
#, elixir-format
msgid "No value provided for required discriminator `%{field}`."
-msgstr ""
+msgstr "没有提供给鉴别器 `%{field}` 提供所需要的值。"
#: lib/pleroma/web/api_spec/render_error.ex:216
#, elixir-format
msgid "Object property count %{property_count} is greater than maxProperties: %{max_properties}."
-msgstr ""
+msgstr "对象属性数 %{property_count} 大于 maxProperties: %{max_properties}。"
#: lib/pleroma/web/api_spec/render_error.ex:224
#, elixir-format
msgid "Object property count %{property_count} is less than minProperties: %{min_properties}"
-msgstr ""
+msgstr "对象属性数 %{property_count} 小于 minProperties: %{min_properties}"
#: lib/pleroma/web/templates/static_fe/static_fe/error.html.eex:2
#, elixir-format
@@ -166,7 +166,7 @@ msgstr "未知的 schema:%{name}。"
#: lib/pleroma/web/api_spec/render_error.ex:192
#, elixir-format
msgid "Value used as discriminator for `%{field}` matches no schemas."
-msgstr ""
+msgstr "用于 `%{field}` 鉴别器的值无法匹配到任何 schema。"
#: lib/pleroma/web/templates/embed/show.html.eex:43
#: lib/pleroma/web/templates/static_fe/static_fe/_notice.html.eex:37
diff --git a/priv/gettext/zh_Hans/LC_MESSAGES/errors.po b/priv/gettext/zh_Hans/LC_MESSAGES/errors.po
index 4431445e3..668472939 100644
--- a/priv/gettext/zh_Hans/LC_MESSAGES/errors.po
+++ b/priv/gettext/zh_Hans/LC_MESSAGES/errors.po
@@ -3,9 +3,9 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-20 13:18+0000\n"
-"PO-Revision-Date: 2022-07-22 19:00+0000\n"
-"Last-Translator: Yating Zhan <thestrandedvalley@protonmail.com>\n"
-"Language-Team: Chinese (Simplified) <http://weblate.pleroma-dev.ebin.club/"
+"PO-Revision-Date: 2024-08-01 08:19+0000\n"
+"Last-Translator: Eric Zhang <ericzhang456@disroot.org>\n"
+"Language-Team: Chinese (Simplified) <https://translate.pleroma.social/"
"projects/pleroma/pleroma-backend-domain-errors/zh_Hans/>\n"
"Language: zh_Hans\n"
"MIME-Version: 1.0\n"
@@ -392,7 +392,7 @@ msgid "Invalid answer data"
msgstr "无效的回答数据"
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33
-#, elixir-format, fuzzy
+#, elixir-format
msgid "Nodeinfo schema version not handled"
msgstr "Nodeinfo schema 版本没被处理"
diff --git a/priv/gettext/zh_Hans/LC_MESSAGES/oauth_scopes.po b/priv/gettext/zh_Hans/LC_MESSAGES/oauth_scopes.po
new file mode 100644
index 000000000..204414836
--- /dev/null
+++ b/priv/gettext/zh_Hans/LC_MESSAGES/oauth_scopes.po
@@ -0,0 +1,274 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2024-08-01 10:12+0300\n"
+"PO-Revision-Date: 2024-08-01 08:19+0000\n"
+"Last-Translator: Eric Zhang <ericzhang456@disroot.org>\n"
+"Language-Team: Chinese (Simplified) <https://translate.pleroma.social/"
+"projects/pleroma/pleroma-backend-domain-oauth_scopes/zh_Hans/>\n"
+"Language: zh_Hans\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 4.13.1\n"
+
+## This file is a PO Template file.
+##
+## "msgid"s here are often extracted from source code.
+## Add new translations manually only if they're dynamic
+## translations that can't be statically extracted.
+##
+## Run "mix gettext.extract" to bring this file up to
+## date. Leave "msgstr"s empty as changing them here has no
+## effect: edit them in PO (.po) files instead.
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin"
+msgstr "全部管理员权限"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:read"
+msgstr "使用管理员 API 读取"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write"
+msgstr "使用管理员 API 写入"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "follow"
+msgstr "读取并写入用户关系"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read"
+msgstr "读取任何信息"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:accounts"
+msgstr "读取所有账号的信息"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:blocks"
+msgstr "读取块关系"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:bookmarks"
+msgstr "读取您的书签"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:favourites"
+msgstr "读取您喜欢的帖子"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:filters"
+msgstr "读取您的过滤器设置"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:follows"
+msgstr "读取关注关系"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:lists"
+msgstr "读取您的列表"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:notifications"
+msgstr "读取您的通知"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:search"
+msgstr "执行搜索"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:statuses"
+msgstr "读取您可以看到的动态"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write"
+msgstr "写入任何信息"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:accounts"
+msgstr "更改您的账号信息"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:blocks"
+msgstr "屏蔽或取消屏蔽任何人"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:bookmarks"
+msgstr "从您的书签中添加或移除"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:conversations"
+msgstr "更改收件人,标记为已阅,或删除聊天"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:favourites"
+msgstr "喜欢或取消喜欢动态"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:filters"
+msgstr "更改您的过滤器设置"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:follows"
+msgstr "关注或取消关注任何人"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:lists"
+msgstr "创建,更改或删除您的列表"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:media"
+msgstr "上传媒体文件或更改您上传的媒体文件"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:mutes"
+msgstr "隐藏或取消隐藏任何人"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:notifications"
+msgstr "标记通知为已读"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:statuses"
+msgstr "发表,编辑,转发帖子或对帖子做出回应"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:read:accounts"
+msgstr "使用管理员 API 读取所有账号"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:read:chats"
+msgstr "使用管理员 API 读取所有聊天"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:read:invites"
+msgstr "使用管理员 API 读取所有邀请码"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:read:media_proxy_caches"
+msgstr "使用管理员 API 读取媒体代理缓存"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:read:reports"
+msgstr "使用管理员 API 读取所有举报"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:read:statuses"
+msgstr "使用管理员 API 读取所有动态"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write:accounts"
+msgstr "使用管理员 API 更改所有账号"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write:chats"
+msgstr "使用管理员 API 更改所有聊天"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write:follows"
+msgstr "使用管理员 API 更改关注关系"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write:invites"
+msgstr "使用管理员 API 创建或吊销邀请码"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write:media_proxy_caches"
+msgstr "使用管理员 API 更改媒体代理缓存"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write:reports"
+msgstr "使用管理员 API 处理举报"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "admin:write:statuses"
+msgstr "使用管理员 API 删除动态,更改动态的范围,或标记为敏感动态"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:mutes"
+msgstr "读取隐藏关系"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "push"
+msgstr "推送通知"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:backups"
+msgstr "读取您的备份"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:chats"
+msgstr "读取您的聊天"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:media"
+msgstr "读取媒体附件"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "read:reports"
+msgstr "读取您的举报"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:chats"
+msgstr "添加或移除聊天信息,或者标记它们为已阅"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:follow"
+msgstr "关注或取消关注任何人"
+
+#: lib/pleroma/web/api_spec/scopes/translator.ex:5
+#, elixir-autogen, elixir-format
+msgid "write:reports"
+msgstr "提交举报"
diff --git a/priv/gettext/zh_Hans/LC_MESSAGES/posix_errors.po b/priv/gettext/zh_Hans/LC_MESSAGES/posix_errors.po
index c486a5486..930f5db1e 100644
--- a/priv/gettext/zh_Hans/LC_MESSAGES/posix_errors.po
+++ b/priv/gettext/zh_Hans/LC_MESSAGES/posix_errors.po
@@ -8,9 +8,9 @@
## to merge POT files into PO files.
msgid ""
msgstr ""
-"PO-Revision-Date: 2022-07-22 19:00+0000\n"
-"Last-Translator: Yating Zhan <thestrandedvalley@protonmail.com>\n"
-"Language-Team: Chinese (Simplified) <http://weblate.pleroma-dev.ebin.club/"
+"PO-Revision-Date: 2024-08-01 08:19+0000\n"
+"Last-Translator: Eric Zhang <ericzhang456@disroot.org>\n"
+"Language-Team: Chinese (Simplified) <https://translate.pleroma.social/"
"projects/pleroma/pleroma-backend-domain-posix_errors/zh_Hans/>\n"
"Language: zh_Hans\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -22,19 +22,19 @@ msgid "eperm"
msgstr "不允许的操作"
msgid "eacces"
-msgstr "权限不够"
+msgstr "拒绝访问"
msgid "eagain"
msgstr "资源暂时不可用"
msgid "ebadf"
-msgstr "坏的文件描述符"
+msgstr "非法的文件描述符"
msgid "ebadmsg"
-msgstr "坏讯息"
+msgstr "非法消息"
msgid "ebusy"
-msgstr "设备或资源忙"
+msgstr "设备或资源繁忙"
msgid "edeadlk"
msgstr "避免了资源死锁"
@@ -46,10 +46,10 @@ msgid "edquot"
msgstr "超出了磁盘配额"
msgid "eexist"
-msgstr "文件存在"
+msgstr "文件已存在"
msgid "efault"
-msgstr "坏地址"
+msgstr "非法地址"
msgid "efbig"
msgstr "文件太大"
@@ -61,7 +61,7 @@ msgid "eintr"
msgstr "系统调用被中断"
msgid "einval"
-msgstr "不合法的参数"
+msgstr "非法参数"
msgid "eio"
msgstr "输入/输出错误"
@@ -79,7 +79,7 @@ msgid "emlink"
msgstr "太多链接"
msgid "emultihop"
-msgstr ""
+msgstr "已尝试多跳"
msgid "enametoolong"
msgstr "文件名太长"
@@ -97,7 +97,7 @@ msgid "enolck"
msgstr "没有可用的锁"
msgid "enolink"
-msgstr "链接被切断了"
+msgstr "链接被切断"
msgid "enoent"
msgstr "没这文件或目录"
@@ -109,19 +109,19 @@ msgid "enospc"
msgstr "设备上没剩余空间"
msgid "enosr"
-msgstr ""
+msgstr "流资源不足"
msgid "enostr"
msgstr "设备不是流"
msgid "enosys"
-msgstr "功能没实现"
+msgstr "功能未实现"
msgid "enotblk"
-msgstr ""
+msgstr "需要块设备"
msgid "enotdir"
-msgstr ""
+msgstr "不是目录"
msgid "enotsup"
msgstr "不受支持的操作"
@@ -136,25 +136,25 @@ msgid "eoverflow"
msgstr "请为给定类型的数据指定较小的数值"
msgid "epipe"
-msgstr ""
+msgstr "管道中断"
msgid "erange"
-msgstr ""
+msgstr "数值超过范围"
msgid "erofs"
-msgstr "只读权限文件系统"
+msgstr "只读文件系统"
msgid "espipe"
-msgstr ""
+msgstr "非法搜寻"
msgid "esrch"
-msgstr "具体进程不存在"
+msgstr "进程不存在"
msgid "estale"
-msgstr ""
+msgstr "过时的文件句柄"
msgid "etxtbsy"
-msgstr "文本文件忙碌"
+msgstr "文本文件繁忙"
msgid "exdev"
-msgstr "该多设备链接不可用"
+msgstr "非法多设备链接"
diff --git a/priv/repo/migrations/20240904142434_assign_app_user.exs b/priv/repo/migrations/20240904142434_assign_app_user.exs
new file mode 100644
index 000000000..11bec529b
--- /dev/null
+++ b/priv/repo/migrations/20240904142434_assign_app_user.exs
@@ -0,0 +1,21 @@
+defmodule Pleroma.Repo.Migrations.AssignAppUser do
+ use Ecto.Migration
+
+ alias Pleroma.Repo
+ alias Pleroma.Web.OAuth.App
+ alias Pleroma.Web.OAuth.Token
+
+ def up do
+ Repo.all(Token)
+ |> Enum.group_by(fn x -> Map.get(x, :app_id) end)
+ |> Enum.each(fn {_app_id, tokens} ->
+ token =
+ Enum.filter(tokens, fn x -> not is_nil(x.user_id) end)
+ |> List.first()
+
+ App.maybe_update_owner(token)
+ end)
+ end
+
+ def down, do: :ok
+end
diff --git a/priv/scrubbers/default.ex b/priv/scrubbers/default.ex
index a75a6465d..dad9dc1a1 100644
--- a/priv/scrubbers/default.ex
+++ b/priv/scrubbers/default.ex
@@ -22,7 +22,8 @@ defmodule Pleroma.HTML.Scrubber.Default do
"u-url",
"mention",
"u-url mention",
- "mention u-url"
+ "mention u-url",
+ "mention hashtag"
])
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
diff --git a/priv/scrubbers/twitter_text.ex b/priv/scrubbers/twitter_text.ex
index 6e23b3efb..4df840735 100644
--- a/priv/scrubbers/twitter_text.ex
+++ b/priv/scrubbers/twitter_text.ex
@@ -23,7 +23,8 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
"u-url",
"mention",
"u-url mention",
- "mention u-url"
+ "mention u-url",
+ "mention hashtag"
])
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
diff --git a/test/fixtures/bastianallgeier.json b/test/fixtures/bastianallgeier.json
deleted file mode 100644
index 6b47e7db9..000000000
--- a/test/fixtures/bastianallgeier.json
+++ /dev/null
@@ -1,117 +0,0 @@
-{
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- {
- "Curve25519Key": "toot:Curve25519Key",
- "Device": "toot:Device",
- "Ed25519Key": "toot:Ed25519Key",
- "Ed25519Signature": "toot:Ed25519Signature",
- "EncryptedMessage": "toot:EncryptedMessage",
- "PropertyValue": "schema:PropertyValue",
- "alsoKnownAs": {
- "@id": "as:alsoKnownAs",
- "@type": "@id"
- },
- "cipherText": "toot:cipherText",
- "claim": {
- "@id": "toot:claim",
- "@type": "@id"
- },
- "deviceId": "toot:deviceId",
- "devices": {
- "@id": "toot:devices",
- "@type": "@id"
- },
- "discoverable": "toot:discoverable",
- "featured": {
- "@id": "toot:featured",
- "@type": "@id"
- },
- "featuredTags": {
- "@id": "toot:featuredTags",
- "@type": "@id"
- },
- "fingerprintKey": {
- "@id": "toot:fingerprintKey",
- "@type": "@id"
- },
- "focalPoint": {
- "@container": "@list",
- "@id": "toot:focalPoint"
- },
- "identityKey": {
- "@id": "toot:identityKey",
- "@type": "@id"
- },
- "indexable": "toot:indexable",
- "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
- "memorial": "toot:memorial",
- "messageFranking": "toot:messageFranking",
- "messageType": "toot:messageType",
- "movedTo": {
- "@id": "as:movedTo",
- "@type": "@id"
- },
- "publicKeyBase64": "toot:publicKeyBase64",
- "schema": "http://schema.org#",
- "suspended": "toot:suspended",
- "toot": "http://joinmastodon.org/ns#",
- "value": "schema:value"
- }
- ],
- "attachment": [
- {
- "name": "Website",
- "type": "PropertyValue",
- "value": "<a href=\"https://bastianallgeier.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">bastianallgeier.com</span><span class=\"invisible\"></span></a>"
- },
- {
- "name": "Project",
- "type": "PropertyValue",
- "value": "<a href=\"https://getkirby.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">getkirby.com</span><span class=\"invisible\"></span></a>"
- },
- {
- "name": "Github",
- "type": "PropertyValue",
- "value": "<a href=\"https://github.com/bastianallgeier\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">github.com/bastianallgeier</span><span class=\"invisible\"></span></a>"
- }
- ],
- "devices": "https://mastodon.social/users/bastianallgeier/collections/devices",
- "discoverable": true,
- "endpoints": {
- "sharedInbox": "https://mastodon.social/inbox"
- },
- "featured": "https://mastodon.social/users/bastianallgeier/collections/featured",
- "featuredTags": "https://mastodon.social/users/bastianallgeier/collections/tags",
- "followers": "https://mastodon.social/users/bastianallgeier/followers",
- "following": "https://mastodon.social/users/bastianallgeier/following",
- "icon": {
- "mediaType": "image/jpeg",
- "type": "Image",
- "url": "https://files.mastodon.social/accounts/avatars/000/007/393/original/0180a20079617c71.jpg"
- },
- "id": "https://mastodon.social/users/bastianallgeier",
- "image": {
- "mediaType": "image/jpeg",
- "type": "Image",
- "url": "https://files.mastodon.social/accounts/headers/000/007/393/original/13d644ab46d50478.jpeg"
- },
- "inbox": "https://mastodon.social/users/bastianallgeier/inbox",
- "indexable": false,
- "manuallyApprovesFollowers": false,
- "memorial": false,
- "name": "Bastian Allgeier",
- "outbox": "https://mastodon.social/users/bastianallgeier/outbox",
- "preferredUsername": "bastianallgeier",
- "publicKey": {
- "id": "https://mastodon.social/users/bastianallgeier#main-key",
- "owner": "https://mastodon.social/users/bastianallgeier",
- "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3fz+hpgVztO9z6HUhyzv\nwP++ERBBoIwSLKf1TyIM8bvzGFm2YXaO5uxu1HvumYFTYc3ACr3q4j8VUb7NMxkQ\nlzu4QwPjOFJ43O+fY+HSPORXEDW5fXDGC5DGpox4+i08LxRmx7L6YPRUSUuPN8nI\nWyq1Qsq1zOQrNY/rohMXkBdSXxqC3yIRqvtLt4otCgay/5tMogJWkkS6ZKyFhb9z\nwVVy1fsbV10c9C+SHy4NH26CKaTtpTYLRBMjhTCS8bX8iDSjGIf2aZgYs1ir7gEz\n9wf5CvLiENmVWGwm64t6KSEAkA4NJ1hzgHUZPCjPHZE2SmhO/oHaxokTzqtbbENJ\n1QIDAQAB\n-----END PUBLIC KEY-----\n"
- },
- "published": "2016-11-01T00:00:00Z",
- "summary": "<p>Designer &amp; developer. Creator of Kirby CMS</p>",
- "tag": [],
- "type": "Person",
- "url": "https://mastodon.social/@bastianallgeier"
-}
diff --git a/test/fixtures/receiver_worker_signature_activity.json b/test/fixtures/receiver_worker_signature_activity.json
index 3c3fb3fd2..19dc0087f 100644
--- a/test/fixtures/receiver_worker_signature_activity.json
+++ b/test/fixtures/receiver_worker_signature_activity.json
@@ -1,62 +1,109 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
{
+ "claim": {
+ "@id": "toot:claim",
+ "@type": "@id"
+ },
+ "memorial": "toot:memorial",
"atomUri": "ostatus:atomUri",
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"blurhash": "toot:blurhash",
- "conversation": "ostatus:conversation",
+ "ostatus": "http://ostatus.org#",
+ "discoverable": "toot:discoverable",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
},
- "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
- "ostatus": "http://ostatus.org#",
+ "votersCount": "toot:votersCount",
+ "Hashtag": "as:Hashtag",
+ "Emoji": "toot:Emoji",
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ },
"sensitive": "as:sensitive",
+ "movedTo": {
+ "@id": "as:movedTo",
+ "@type": "@id"
+ },
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "conversation": "ostatus:conversation",
+ "Device": "toot:Device",
+ "schema": "http://schema.org#",
"toot": "http://joinmastodon.org/ns#",
- "votersCount": "toot:votersCount"
- }
- ],
- "atomUri": "https://chaos.social/users/distantnative/statuses/109336635639931467",
- "attachment": [
- {
- "blurhash": "UAK1zS00OXIUxuMxIUM{?b-:-;W:Di?b%2M{",
- "height": 960,
- "mediaType": "image/jpeg",
- "name": null,
- "type": "Document",
- "url": "https://assets.chaos.social/media_attachments/files/109/336/634/286/114/657/original/2e6122063d8bfb26.jpeg",
- "width": 346
+ "cipherText": "toot:cipherText",
+ "suspended": "toot:suspended",
+ "messageType": "toot:messageType",
+ "featuredTags": {
+ "@id": "toot:featuredTags",
+ "@type": "@id"
+ },
+ "Curve25519Key": "toot:Curve25519Key",
+ "deviceId": "toot:deviceId",
+ "Ed25519Signature": "toot:Ed25519Signature",
+ "featured": {
+ "@id": "toot:featured",
+ "@type": "@id"
+ },
+ "devices": {
+ "@id": "toot:devices",
+ "@type": "@id"
+ },
+ "value": "schema:value",
+ "PropertyValue": "schema:PropertyValue",
+ "messageFranking": "toot:messageFranking",
+ "publicKeyBase64": "toot:publicKeyBase64",
+ "identityKey": {
+ "@id": "toot:identityKey",
+ "@type": "@id"
+ },
+ "Ed25519Key": "toot:Ed25519Key",
+ "indexable": "toot:indexable",
+ "EncryptedMessage": "toot:EncryptedMessage",
+ "fingerprintKey": {
+ "@id": "toot:fingerprintKey",
+ "@type": "@id"
+ }
}
],
- "attributedTo": "https://chaos.social/users/distantnative",
- "cc": [
- "https://chaos.social/users/distantnative/followers"
- ],
- "content": "<p>Favorite piece of anthropology meta discourse.</p>",
- "contentMap": {
- "en": "<p>Favorite piece of anthropology meta discourse.</p>"
- },
- "conversation": "tag:chaos.social,2022-11-13:objectId=71843781:objectType=Conversation",
- "id": "https://chaos.social/users/distantnative/statuses/109336635639931467",
+ "actor": "https://phpc.social/users/denniskoch",
+ "cc": [],
+ "id": "https://phpc.social/users/denniskoch/statuses/112847382711461301/activity",
"inReplyTo": null,
"inReplyToAtomUri": null,
- "published": "2022-11-13T13:04:20Z",
- "replies": {
- "first": {
- "items": [],
- "next": "https://chaos.social/users/distantnative/statuses/109336635639931467/replies?only_other_accounts=true&page=true",
- "partOf": "https://chaos.social/users/distantnative/statuses/109336635639931467/replies",
- "type": "CollectionPage"
+ "object": {
+ "atomUri": "https://phpc.social/users/denniskoch/statuses/112847382711461301",
+ "attachment": [],
+ "attributedTo": "https://phpc.social/users/denniskoch",
+ "cc": [],
+ "content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://mastodon.social/@bastianallgeier\" class=\"u-url mention\">@<span>bastianallgeier</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://chaos.social/@distantnative\" class=\"u-url mention\">@<span>distantnative</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://fosstodon.org/@kev\" class=\"u-url mention\">@<span>kev</span></a></span> Another main argument: Discord is popular. Many people have an account, so you can just join an server quickly. Also you know the app and how to get around.</p>",
+ "contentMap": {
+ "en": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://mastodon.social/@bastianallgeier\" class=\"u-url mention\">@<span>bastianallgeier</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://chaos.social/@distantnative\" class=\"u-url mention\">@<span>distantnative</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://fosstodon.org/@kev\" class=\"u-url mention\">@<span>kev</span></a></span> Another main argument: Discord is popular. Many people have an account, so you can just join an server quickly. Also you know the app and how to get around.</p>"
},
- "id": "https://chaos.social/users/distantnative/statuses/109336635639931467/replies",
- "type": "Collection"
+ "conversation": "tag:mastodon.social,2024-07-25:objectId=760068442:objectType=Conversation",
+ "id": "https://phpc.social/users/denniskoch/statuses/112847382711461301",
+ "published": "2024-07-25T13:33:29Z",
+ "replies": null,
+ "sensitive": false,
+ "tag": [],
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "type": "Note",
+ "url": "https://phpc.social/@denniskoch/112847382711461301"
+ },
+ "published": "2024-07-25T13:33:29Z",
+ "signature": {
+ "created": "2024-07-25T13:33:29Z",
+ "creator": "https://phpc.social/users/denniskoch#main-key",
+ "signatureValue": "slz9BKJzd2n1S44wdXGOU+bV/wsskdgAaUpwxj8R16mYOL8+DTpE6VnfSKoZGsBBJT8uG5gnVfVEz1YsTUYtymeUgLMh7cvd8VnJnZPS+oixbmBRVky/Myf91TEgQQE7G4vDmTdB4ii54hZrHcOOYYf5FKPNRSkMXboKA6LMqNtekhbI+JTUJYIB02WBBK6PUyo15f6B1RJ6HGWVgud9NE0y1EZXfrkqUt682p8/9D49ORf7AwjXUJibKic2RbPvhEBj70qUGfBm4vvgdWhSUn1IG46xh+U0+NrTSUED82j1ZVOeua/2k/igkGs8cSBkY35quXTkPz6gbqCCH66CuA==",
+ "type": "RsaSignature2017"
},
- "sensitive": false,
- "summary": null,
- "tag": [],
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
- "type": "Note",
- "url": "https://chaos.social/@distantnative/109336635639931467"
+ "type": "Create"
}
diff --git a/test/pleroma/html_test.exs b/test/pleroma/html_test.exs
index 1be161971..d17b07540 100644
--- a/test/pleroma/html_test.exs
+++ b/test/pleroma/html_test.exs
@@ -41,6 +41,10 @@ defmodule Pleroma.HTMLTest do
<span class="h-card"><a class="u-url mention animate-spin">@<span>foo</span></a></span>
"""
+ @mention_hashtags_sample """
+ <a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
+ """
+
describe "StripTags scrubber" do
test "works as expected" do
expected = """
@@ -126,6 +130,15 @@ defmodule Pleroma.HTMLTest do
Pleroma.HTML.Scrubber.TwitterText
)
end
+
+ test "does allow mention hashtags" do
+ expected = """
+ <a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
+ """
+
+ assert expected ==
+ HTML.filter_tags(@mention_hashtags_sample, Pleroma.HTML.Scrubber.Default)
+ end
end
describe "default scrubber" do
@@ -189,6 +202,15 @@ defmodule Pleroma.HTMLTest do
Pleroma.HTML.Scrubber.Default
)
end
+
+ test "does allow mention hashtags" do
+ expected = """
+ <a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
+ """
+
+ assert expected ==
+ HTML.filter_tags(@mention_hashtags_sample, Pleroma.HTML.Scrubber.Default)
+ end
end
describe "extract_first_external_url_from_object" do
diff --git a/test/pleroma/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs
index 6704c18db..215fca570 100644
--- a/test/pleroma/object/fetcher_test.exs
+++ b/test/pleroma/object/fetcher_test.exs
@@ -100,7 +100,7 @@ defmodule Pleroma.Object.FetcherTest do
test "it returns thread depth exceeded error if thread depth is exceeded" do
clear_config([:instance, :federation_incoming_replies_max_depth], 0)
- assert {:error, :allowed_depth} = Fetcher.fetch_object_from_id(@ap_id, depth: 1)
+ assert {:allowed_depth, false} = Fetcher.fetch_object_from_id(@ap_id, depth: 1)
end
test "it fetches object if max thread depth is restricted to 0 and depth is not specified" do
@@ -118,15 +118,18 @@ defmodule Pleroma.Object.FetcherTest do
describe "actor origin containment" do
test "it rejects objects with a bogus origin" do
- {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json")
+ {:containment, :error} =
+ Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json")
end
test "it rejects objects when attributedTo is wrong (variant 1)" do
- {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json")
+ {:containment, :error} =
+ Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json")
end
test "it rejects objects when attributedTo is wrong (variant 2)" do
- {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json")
+ {:containment, :error} =
+ Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json")
end
end
@@ -150,14 +153,14 @@ defmodule Pleroma.Object.FetcherTest do
clear_config([:mrf_keyword, :reject], ["yeah"])
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
- assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
+ assert {:transmogrifier, {:reject, "[KeywordPolicy] Matches with rejected keyword"}} ==
Fetcher.fetch_object_from_id(
"http://mastodon.example.org/@admin/99541947525187367"
)
end
test "it does not fetch a spoofed object uploaded on an instance as an attachment" do
- assert {:error, _} =
+ assert {:fetch, {:error, {:content_type, "application/json"}}} =
Fetcher.fetch_object_from_id(
"https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json"
)
diff --git a/test/pleroma/user/import_test.exs b/test/pleroma/user/import_test.exs
index f75305e0e..1d6469a4f 100644
--- a/test/pleroma/user/import_test.exs
+++ b/test/pleroma/user/import_test.exs
@@ -25,11 +25,12 @@ defmodule Pleroma.User.ImportTest do
user3.nickname
]
- {:ok, job} = User.Import.follow_import(user1, identifiers)
+ {:ok, jobs} = User.Import.follows_import(user1, identifiers)
+
+ for job <- jobs do
+ assert {:ok, %User{}} = ObanHelpers.perform(job)
+ end
- assert {:ok, result} = ObanHelpers.perform(job)
- assert is_list(result)
- assert result == [refresh_record(user2), refresh_record(user3)]
assert User.following?(user1, user2)
assert User.following?(user1, user3)
end
@@ -44,11 +45,12 @@ defmodule Pleroma.User.ImportTest do
user3.nickname
]
- {:ok, job} = User.Import.blocks_import(user1, identifiers)
+ {:ok, jobs} = User.Import.blocks_import(user1, identifiers)
+
+ for job <- jobs do
+ assert {:ok, %User{}} = ObanHelpers.perform(job)
+ end
- assert {:ok, result} = ObanHelpers.perform(job)
- assert is_list(result)
- assert result == [user2, user3]
assert User.blocks?(user1, user2)
assert User.blocks?(user1, user3)
end
@@ -63,11 +65,12 @@ defmodule Pleroma.User.ImportTest do
user3.nickname
]
- {:ok, job} = User.Import.mutes_import(user1, identifiers)
+ {:ok, jobs} = User.Import.mutes_import(user1, identifiers)
+
+ for job <- jobs do
+ assert {:ok, %User{}} = ObanHelpers.perform(job)
+ end
- assert {:ok, result} = ObanHelpers.perform(job)
- assert is_list(result)
- assert result == [user2, user3]
assert User.mutes?(user1, user2)
assert User.mutes?(user1, user3)
end
diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
index af1a32fed..3bd589f49 100644
--- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
@@ -657,7 +657,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
end
test "without valid signature, " <>
- "it only accepts Create activities and requires enabled federation",
+ "it accepts Create activities and requires enabled federation",
%{conn: conn} do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
@@ -684,6 +684,54 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|> json_response(400)
end
+ # When activity is delivered to the inbox and we cannot immediately verify signature
+ # we capture all the params and process it later in the Oban job.
+ # Once we begin processing it through Oban we risk fetching the actor to validate the
+ # activity which just leads to inserting a new user to process a Delete not relevant to us.
+ test "Activities of certain types from an unknown actor are discarded", %{conn: conn} do
+ example_bad_types =
+ Pleroma.Constants.activity_types() --
+ Pleroma.Constants.allowed_activity_types_from_strangers()
+
+ Enum.each(example_bad_types, fn bad_type ->
+ params =
+ %{
+ "type" => bad_type,
+ "actor" => "https://unknown.mastodon.instance/users/somebody"
+ }
+ |> Jason.encode!()
+
+ conn
+ |> assign(:valid_signature, false)
+ |> put_req_header("content-type", "application/activity+json")
+ |> post("/inbox", params)
+ |> json_response(400)
+
+ assert all_enqueued() == []
+ end)
+ end
+
+ test "Unknown activity types are discarded", %{conn: conn} do
+ unknown_types = ["Poke", "Read", "Dazzle"]
+
+ Enum.each(unknown_types, fn bad_type ->
+ params =
+ %{
+ "type" => bad_type,
+ "actor" => "https://unknown.mastodon.instance/users/somebody"
+ }
+ |> Jason.encode!()
+
+ conn
+ |> assign(:valid_signature, true)
+ |> put_req_header("content-type", "application/activity+json")
+ |> post("/inbox", params)
+ |> json_response(400)
+
+ assert all_enqueued() == []
+ end)
+ end
+
test "accepts Add/Remove activities", %{conn: conn} do
object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"
diff --git a/test/pleroma/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs
index 3acbac396..99ed42877 100644
--- a/test/pleroma/web/activity_pub/publisher_test.exs
+++ b/test/pleroma/web/activity_pub/publisher_test.exs
@@ -180,7 +180,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
Publisher.prepare_one(%{
inbox: inbox,
activity_id: activity.id,
- unreachable_since: NaiveDateTime.utc_now()
+ unreachable_since: NaiveDateTime.utc_now() |> NaiveDateTime.to_string()
})
|> Publisher.publish_one()
@@ -269,7 +269,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
Publisher.prepare_one(%{
inbox: inbox,
activity_id: activity.id,
- unreachable_since: NaiveDateTime.utc_now()
+ unreachable_since: NaiveDateTime.utc_now() |> NaiveDateTime.to_string()
})
|> Publisher.publish_one()
end) =~ "connrefused"
diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs
index 0301a4cca..5d24c0e9f 100644
--- a/test/pleroma/web/mastodon_api/views/account_view_test.exs
+++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs
@@ -460,6 +460,45 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
test_relationship_rendering(user, other_user, expected)
end
+ test "relationship does not indicate following if a FollowingRelationship is missing" do
+ user = insert(:user)
+ other_user = insert(:user, local: false)
+
+ # Create a follow relationship with the real Follow Activity and Accept it
+ assert {:ok, _, _, _} = CommonAPI.follow(other_user, user)
+ assert {:ok, _} = CommonAPI.accept_follow_request(user, other_user)
+
+ assert %{data: %{"state" => "accept"}} =
+ Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, other_user)
+
+ # Fetch the relationship and forcibly delete it to simulate
+ # a Follow Accept that did not complete processing
+ %{following_relationships: [relationship]} =
+ Pleroma.UserRelationship.view_relationships_option(user, [other_user])
+
+ assert {:ok, _} = Pleroma.Repo.delete(relationship)
+
+ assert %{following_relationships: [], user_relationships: []} ==
+ Pleroma.UserRelationship.view_relationships_option(user, [other_user])
+
+ expected =
+ Map.merge(
+ @blank_response,
+ %{
+ following: false,
+ followed_by: false,
+ muting: false,
+ muting_notifications: false,
+ subscribing: false,
+ notifying: false,
+ showing_reblogs: true,
+ id: to_string(other_user.id)
+ }
+ )
+
+ test_relationship_rendering(user, other_user, expected)
+ end
+
test "represent a relationship for the blocking and blocked user" do
user = insert(:user)
other_user = insert(:user)
diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs
index afe0ccb28..bc6dec32a 100644
--- a/test/pleroma/web/mastodon_api/views/status_view_test.exs
+++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs
@@ -342,7 +342,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
parent_visible: false,
pinned_at: nil,
quotes_count: 0,
- bookmark_folder: nil
+ bookmark_folder: nil,
+ list_id: nil
}
}
@@ -912,6 +913,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
status = StatusView.render("show.json", activity: activity)
assert status.visibility == "list"
+ assert status.pleroma.list_id == nil
+
+ status = StatusView.render("show.json", activity: activity, for: user)
+
+ assert status.pleroma.list_id == list.id
end
test "has a field for parent visibility" do
diff --git a/test/pleroma/web/node_info_test.exs b/test/pleroma/web/node_info_test.exs
index f474220be..afe4ebb36 100644
--- a/test/pleroma/web/node_info_test.exs
+++ b/test/pleroma/web/node_info_test.exs
@@ -24,6 +24,19 @@ defmodule Pleroma.Web.NodeInfoTest do
|> get(href)
|> json_response(200)
end)
+
+ accept_types = [
+ "application/activity+json",
+ "application/json",
+ "application/jrd+json"
+ ]
+
+ for type <- accept_types do
+ conn
+ |> put_req_header("accept", type)
+ |> get("/.well-known/nodeinfo")
+ |> json_response(200)
+ end
end
test "nodeinfo shows staff accounts", %{conn: conn} do
diff --git a/test/pleroma/web/o_auth/app_test.exs b/test/pleroma/web/o_auth/app_test.exs
index 96a67de6b..44219cf90 100644
--- a/test/pleroma/web/o_auth/app_test.exs
+++ b/test/pleroma/web/o_auth/app_test.exs
@@ -53,4 +53,21 @@ defmodule Pleroma.Web.OAuth.AppTest do
assert Enum.sort(App.get_user_apps(user)) == Enum.sort(apps)
end
+
+ test "removes orphaned apps" do
+ attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
+ {:ok, %App{} = old_app} = App.get_or_make(attrs, ["write"])
+
+ attrs = %{client_name: "PleromaFE", redirect_uris: "."}
+ {:ok, %App{} = app} = App.get_or_make(attrs, ["write"])
+
+ # backdate the old app so it's within the threshold for being cleaned up
+ {:ok, _} =
+ "UPDATE apps SET inserted_at = now() - interval '1 hour' WHERE id = #{old_app.id}"
+ |> Pleroma.Repo.query()
+
+ App.remove_orphans()
+
+ assert [app] == Pleroma.Repo.all(App)
+ end
end
diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs
index 83a08d9fc..260442771 100644
--- a/test/pleroma/web/o_auth/o_auth_controller_test.exs
+++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
alias Pleroma.MFA.TOTP
alias Pleroma.Repo
alias Pleroma.User
+ alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.OAuthController
alias Pleroma.Web.OAuth.Token
@@ -770,6 +771,9 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
{:ok, auth} = Authorization.create_authorization(app, user, ["write"])
+ # Verify app has no associated user yet
+ assert %Pleroma.Web.OAuth.App{user_id: nil} = Repo.get_by(App, %{id: app.id})
+
conn =
build_conn()
|> post("/oauth/token", %{
@@ -786,6 +790,10 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
assert token
assert token.scopes == auth.scopes
assert user.ap_id == ap_id
+
+ # Verify app has an associated user now
+ user_id = user.id
+ assert %Pleroma.Web.OAuth.App{user_id: ^user_id} = Repo.get_by(App, %{id: app.id})
end
test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
diff --git a/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs
index 52a62e416..efdc743e3 100644
--- a/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs
+++ b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
test "it returns HTTP 200", %{conn: conn} do
user2 = insert(:user)
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"})
@@ -38,7 +38,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
"Account address,Show boosts\n#{user2.ap_id},true"
end}
]) do
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/follow_import", %{
@@ -46,9 +46,9 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
})
|> json_response_and_validate_schema(200)
- assert [{:ok, job_result}] = ObanHelpers.perform_all()
- assert job_result == [refresh_record(user2)]
- assert [%Pleroma.User{follower_count: 1}] = job_result
+ assert [{:ok, updated_user}] = ObanHelpers.perform_all()
+ assert updated_user.id == user2.id
+ assert updated_user.follower_count == 1
end
end
@@ -63,7 +63,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
})
|> json_response_and_validate_schema(200)
- assert response == "job started"
+ assert response == "jobs started"
end
test "requires 'follow' or 'write:follows' permissions" do
@@ -102,14 +102,20 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
]
|> Enum.join("\n")
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/follow_import", %{"list" => identifiers})
|> json_response_and_validate_schema(200)
- assert [{:ok, job_result}] = ObanHelpers.perform_all()
- assert job_result == Enum.map(users, &refresh_record/1)
+ results = ObanHelpers.perform_all()
+
+ returned_users =
+ for {_, returned_user} <- results do
+ returned_user
+ end
+
+ assert returned_users == Enum.map(users, &refresh_record/1)
end
end
@@ -120,7 +126,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
test "it returns HTTP 200", %{conn: conn} do
user2 = insert(:user)
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"})
@@ -133,7 +139,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
with_mocks([
{File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end}
]) do
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/blocks_import", %{
@@ -141,8 +147,14 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
})
|> json_response_and_validate_schema(200)
- assert [{:ok, job_result}] = ObanHelpers.perform_all()
- assert job_result == users
+ results = ObanHelpers.perform_all()
+
+ returned_users =
+ for {_, returned_user} <- results do
+ returned_user
+ end
+
+ assert returned_users == users
end
end
@@ -159,14 +171,25 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
]
|> Enum.join(" ")
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/blocks_import", %{"list" => identifiers})
|> json_response_and_validate_schema(200)
- assert [{:ok, job_result}] = ObanHelpers.perform_all()
- assert job_result == users
+ results = ObanHelpers.perform_all()
+
+ returned_user_ids =
+ for {_, user} <- results do
+ user.id
+ end
+
+ original_user_ids =
+ for user <- users do
+ user.id
+ end
+
+ assert match?(^original_user_ids, returned_user_ids)
end
end
@@ -177,24 +200,25 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
test "it returns HTTP 200", %{user: user, conn: conn} do
user2 = insert(:user)
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/mutes_import", %{"list" => "#{user2.ap_id}"})
|> json_response_and_validate_schema(200)
- assert [{:ok, job_result}] = ObanHelpers.perform_all()
- assert job_result == [user2]
+ [{:ok, result_user}] = ObanHelpers.perform_all()
+
+ assert result_user == refresh_record(user2)
assert Pleroma.User.mutes?(user, user2)
end
test "it imports mutes users from file", %{user: user, conn: conn} do
- users = [user2, user3] = insert_list(2, :user)
+ [user2, user3] = insert_list(2, :user)
with_mocks([
{File, [], read!: fn "mutes_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end}
]) do
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/mutes_import", %{
@@ -202,14 +226,19 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
})
|> json_response_and_validate_schema(200)
- assert [{:ok, job_result}] = ObanHelpers.perform_all()
- assert job_result == users
- assert Enum.all?(users, &Pleroma.User.mutes?(user, &1))
+ results = ObanHelpers.perform_all()
+
+ returned_users =
+ for {_, returned_user} <- results do
+ returned_user
+ end
+
+ assert Enum.all?(returned_users, &Pleroma.User.mutes?(user, &1))
end
end
test "it imports mutes with different nickname variations", %{user: user, conn: conn} do
- users = [user2, user3, user4, user5, user6] = insert_list(5, :user)
+ [user2, user3, user4, user5, user6] = insert_list(5, :user)
identifiers =
[
@@ -221,15 +250,20 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
]
|> Enum.join(" ")
- assert "job started" ==
+ assert "jobs started" ==
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/mutes_import", %{"list" => identifiers})
|> json_response_and_validate_schema(200)
- assert [{:ok, job_result}] = ObanHelpers.perform_all()
- assert job_result == users
- assert Enum.all?(users, &Pleroma.User.mutes?(user, &1))
+ results = ObanHelpers.perform_all()
+
+ returned_users =
+ for {_, returned_user} <- results do
+ returned_user
+ end
+
+ assert Enum.all?(returned_users, &Pleroma.User.mutes?(user, &1))
end
end
end
diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs
index b8acd01c5..bdbf3de32 100644
--- a/test/pleroma/web/plugs/authentication_plug_test.exs
+++ b/test/pleroma/web/plugs/authentication_plug_test.exs
@@ -70,6 +70,24 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
assert "$pbkdf2" <> _ = user.password_hash
end
+ test "with an argon2 hash, it updates to a pkbdf2 hash", %{conn: conn} do
+ user = insert(:user, password_hash: Argon2.hash_pwd_salt("123"))
+ assert "$argon2" <> _ = user.password_hash
+
+ conn =
+ conn
+ |> assign(:auth_user, user)
+ |> assign(:auth_credentials, %{password: "123"})
+ |> AuthenticationPlug.call(%{})
+
+ assert conn.assigns.user.id == conn.assigns.auth_user.id
+ assert conn.assigns.token == nil
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
+
+ user = User.get_by_id(user.id)
+ assert "$pbkdf2" <> _ = user.password_hash
+ end
+
describe "checkpw/2" do
test "check pbkdf2 hash" do
hash =
@@ -86,6 +104,14 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
refute AuthenticationPlug.checkpw("password1", hash)
end
+ test "check argon2 hash" do
+ hash =
+ "$argon2id$v=19$m=65536,t=8,p=2$zEMMsTuK5KkL5AFWbX7jyQ$VyaQD7PF6e9btz0oH1YiAkWwIGZ7WNDZP8l+a/O171g"
+
+ assert AuthenticationPlug.checkpw("password", hash)
+ refute AuthenticationPlug.checkpw("password1", hash)
+ end
+
test "it returns false when hash invalid" do
hash =
"psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
diff --git a/test/pleroma/workers/receiver_worker_test.exs b/test/pleroma/workers/receiver_worker_test.exs
index 33be91085..4d53c44ed 100644
--- a/test/pleroma/workers/receiver_worker_test.exs
+++ b/test/pleroma/workers/receiver_worker_test.exs
@@ -9,6 +9,7 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
import Mock
import Pleroma.Factory
+ alias Pleroma.User
alias Pleroma.Web.Federator
alias Pleroma.Workers.ReceiverWorker
@@ -51,25 +52,106 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
})
end
- test "it can validate the signature" do
- Tesla.Mock.mock(fn
- %{url: "https://mastodon.social/users/bastianallgeier"} ->
- %Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/bastianallgeier.json"),
- headers: [{"content-type", "application/activity+json"}]
- }
+ describe "cancels on a failed user fetch" do
+ setup do
+ Tesla.Mock.mock(fn
+ %{url: "https://springfield.social/users/bart"} ->
+ %Tesla.Env{
+ status: 403,
+ body: ""
+ }
- %{url: "https://mastodon.social/users/bastianallgeier/collections/featured"} ->
- %Tesla.Env{
- status: 200,
- headers: [{"content-type", "application/activity+json"}],
- body:
- File.read!("test/fixtures/users_mock/masto_featured.json")
- |> String.replace("{{domain}}", "mastodon.social")
- |> String.replace("{{nickname}}", "bastianallgeier")
- }
+ %{url: "https://springfield.social/users/troymcclure"} ->
+ %Tesla.Env{
+ status: 404,
+ body: ""
+ }
+
+ %{url: "https://springfield.social/users/hankscorpio"} ->
+ %Tesla.Env{
+ status: 410,
+ body: ""
+ }
+ end)
+ end
+
+ test "when request returns a 403" do
+ params =
+ insert(:note_activity).data
+ |> Map.put("actor", "https://springfield.social/users/bart")
+
+ {:ok, oban_job} =
+ Federator.incoming_ap_doc(%{
+ method: "POST",
+ req_headers: [],
+ request_path: "/inbox",
+ params: params,
+ query_string: ""
+ })
+
+ assert {:cancel, {:error, :forbidden}} = ReceiverWorker.perform(oban_job)
+ end
+ test "when request returns a 404" do
+ params =
+ insert(:note_activity).data
+ |> Map.put("actor", "https://springfield.social/users/troymcclure")
+
+ {:ok, oban_job} =
+ Federator.incoming_ap_doc(%{
+ method: "POST",
+ req_headers: [],
+ request_path: "/inbox",
+ params: params,
+ query_string: ""
+ })
+
+ assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job)
+ end
+
+ test "when request returns a 410" do
+ params =
+ insert(:note_activity).data
+ |> Map.put("actor", "https://springfield.social/users/hankscorpio")
+
+ {:ok, oban_job} =
+ Federator.incoming_ap_doc(%{
+ method: "POST",
+ req_headers: [],
+ request_path: "/inbox",
+ params: params,
+ query_string: ""
+ })
+
+ assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job)
+ end
+
+ test "when user account is disabled" do
+ user = insert(:user)
+
+ fake_activity = URI.parse(user.ap_id) |> Map.put(:path, "/fake-activity") |> to_string
+
+ params =
+ insert(:note_activity, user: user).data
+ |> Map.put("id", fake_activity)
+
+ {:ok, %User{}} = User.set_activation(user, false)
+
+ {:ok, oban_job} =
+ Federator.incoming_ap_doc(%{
+ method: "POST",
+ req_headers: [],
+ request_path: "/inbox",
+ params: params,
+ query_string: ""
+ })
+
+ assert {:cancel, {:user_active, false}} = ReceiverWorker.perform(oban_job)
+ end
+ end
+
+ test "it can validate the signature" do
+ Tesla.Mock.mock(fn
%{url: "https://phpc.social/users/denniskoch"} ->
%Tesla.Env{
status: 200,
@@ -86,136 +168,10 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|> String.replace("{{domain}}", "phpc.social")
|> String.replace("{{nickname}}", "denniskoch")
}
-
- %{url: "https://mastodon.social/users/bastianallgeier/statuses/112846516276907281"} ->
- %Tesla.Env{
- status: 200,
- headers: [{"content-type", "application/activity+json"}],
- body: File.read!("test/fixtures/receiver_worker_signature_activity.json")
- }
end)
- params = %{
- "@context" => [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1",
- %{
- "claim" => %{"@id" => "toot:claim", "@type" => "@id"},
- "memorial" => "toot:memorial",
- "atomUri" => "ostatus:atomUri",
- "manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
- "blurhash" => "toot:blurhash",
- "ostatus" => "http://ostatus.org#",
- "discoverable" => "toot:discoverable",
- "focalPoint" => %{"@container" => "@list", "@id" => "toot:focalPoint"},
- "votersCount" => "toot:votersCount",
- "Hashtag" => "as:Hashtag",
- "Emoji" => "toot:Emoji",
- "alsoKnownAs" => %{"@id" => "as:alsoKnownAs", "@type" => "@id"},
- "sensitive" => "as:sensitive",
- "movedTo" => %{"@id" => "as:movedTo", "@type" => "@id"},
- "inReplyToAtomUri" => "ostatus:inReplyToAtomUri",
- "conversation" => "ostatus:conversation",
- "Device" => "toot:Device",
- "schema" => "http://schema.org#",
- "toot" => "http://joinmastodon.org/ns#",
- "cipherText" => "toot:cipherText",
- "suspended" => "toot:suspended",
- "messageType" => "toot:messageType",
- "featuredTags" => %{"@id" => "toot:featuredTags", "@type" => "@id"},
- "Curve25519Key" => "toot:Curve25519Key",
- "deviceId" => "toot:deviceId",
- "Ed25519Signature" => "toot:Ed25519Signature",
- "featured" => %{"@id" => "toot:featured", "@type" => "@id"},
- "devices" => %{"@id" => "toot:devices", "@type" => "@id"},
- "value" => "schema:value",
- "PropertyValue" => "schema:PropertyValue",
- "messageFranking" => "toot:messageFranking",
- "publicKeyBase64" => "toot:publicKeyBase64",
- "identityKey" => %{"@id" => "toot:identityKey", "@type" => "@id"},
- "Ed25519Key" => "toot:Ed25519Key",
- "indexable" => "toot:indexable",
- "EncryptedMessage" => "toot:EncryptedMessage",
- "fingerprintKey" => %{"@id" => "toot:fingerprintKey", "@type" => "@id"}
- }
- ],
- "actor" => "https://phpc.social/users/denniskoch",
- "cc" => [
- "https://phpc.social/users/denniskoch/followers",
- "https://mastodon.social/users/bastianallgeier",
- "https://chaos.social/users/distantnative",
- "https://fosstodon.org/users/kev"
- ],
- "id" => "https://phpc.social/users/denniskoch/statuses/112847382711461301/activity",
- "object" => %{
- "atomUri" => "https://phpc.social/users/denniskoch/statuses/112847382711461301",
- "attachment" => [],
- "attributedTo" => "https://phpc.social/users/denniskoch",
- "cc" => [
- "https://phpc.social/users/denniskoch/followers",
- "https://mastodon.social/users/bastianallgeier",
- "https://chaos.social/users/distantnative",
- "https://fosstodon.org/users/kev"
- ],
- "content" =>
- "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://mastodon.social/@bastianallgeier\" class=\"u-url mention\">@<span>bastianallgeier</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://chaos.social/@distantnative\" class=\"u-url mention\">@<span>distantnative</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://fosstodon.org/@kev\" class=\"u-url mention\">@<span>kev</span></a></span> Another main argument: Discord is popular. Many people have an account, so you can just join an server quickly. Also you know the app and how to get around.</p>",
- "contentMap" => %{
- "en" =>
- "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://mastodon.social/@bastianallgeier\" class=\"u-url mention\">@<span>bastianallgeier</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://chaos.social/@distantnative\" class=\"u-url mention\">@<span>distantnative</span></a></span> <span class=\"h-card\" translate=\"no\"><a href=\"https://fosstodon.org/@kev\" class=\"u-url mention\">@<span>kev</span></a></span> Another main argument: Discord is popular. Many people have an account, so you can just join an server quickly. Also you know the app and how to get around.</p>"
- },
- "conversation" =>
- "tag:mastodon.social,2024-07-25:objectId=760068442:objectType=Conversation",
- "id" => "https://phpc.social/users/denniskoch/statuses/112847382711461301",
- "inReplyTo" =>
- "https://mastodon.social/users/bastianallgeier/statuses/112846516276907281",
- "inReplyToAtomUri" =>
- "https://mastodon.social/users/bastianallgeier/statuses/112846516276907281",
- "published" => "2024-07-25T13:33:29Z",
- "replies" => %{
- "first" => %{
- "items" => [],
- "next" =>
- "https://phpc.social/users/denniskoch/statuses/112847382711461301/replies?only_other_accounts=true&page=true",
- "partOf" =>
- "https://phpc.social/users/denniskoch/statuses/112847382711461301/replies",
- "type" => "CollectionPage"
- },
- "id" => "https://phpc.social/users/denniskoch/statuses/112847382711461301/replies",
- "type" => "Collection"
- },
- "sensitive" => false,
- "tag" => [
- %{
- "href" => "https://mastodon.social/users/bastianallgeier",
- "name" => "@bastianallgeier@mastodon.social",
- "type" => "Mention"
- },
- %{
- "href" => "https://chaos.social/users/distantnative",
- "name" => "@distantnative@chaos.social",
- "type" => "Mention"
- },
- %{
- "href" => "https://fosstodon.org/users/kev",
- "name" => "@kev@fosstodon.org",
- "type" => "Mention"
- }
- ],
- "to" => ["https://www.w3.org/ns/activitystreams#Public"],
- "type" => "Note",
- "url" => "https://phpc.social/@denniskoch/112847382711461301"
- },
- "published" => "2024-07-25T13:33:29Z",
- "signature" => %{
- "created" => "2024-07-25T13:33:29Z",
- "creator" => "https://phpc.social/users/denniskoch#main-key",
- "signatureValue" =>
- "slz9BKJzd2n1S44wdXGOU+bV/wsskdgAaUpwxj8R16mYOL8+DTpE6VnfSKoZGsBBJT8uG5gnVfVEz1YsTUYtymeUgLMh7cvd8VnJnZPS+oixbmBRVky/Myf91TEgQQE7G4vDmTdB4ii54hZrHcOOYYf5FKPNRSkMXboKA6LMqNtekhbI+JTUJYIB02WBBK6PUyo15f6B1RJ6HGWVgud9NE0y1EZXfrkqUt682p8/9D49ORf7AwjXUJibKic2RbPvhEBj70qUGfBm4vvgdWhSUn1IG46xh+U0+NrTSUED82j1ZVOeua/2k/igkGs8cSBkY35quXTkPz6gbqCCH66CuA==",
- "type" => "RsaSignature2017"
- },
- "to" => ["https://www.w3.org/ns/activitystreams#Public"],
- "type" => "Create"
- }
+ params =
+ File.read!("test/fixtures/receiver_worker_signature_activity.json") |> Jason.decode!()
req_headers = [
["accept-encoding", "gzip"],
@@ -245,4 +201,46 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
assert {:ok, %Pleroma.Activity{}} = ReceiverWorker.perform(oban_job)
end
+
+ test "cancels due to origin containment" do
+ params =
+ insert(:note_activity).data
+ |> Map.put("id", "https://notorigindomain.com/activity")
+
+ {:ok, oban_job} =
+ Federator.incoming_ap_doc(%{
+ method: "POST",
+ req_headers: [],
+ request_path: "/inbox",
+ params: params,
+ query_string: ""
+ })
+
+ assert {:cancel, :origin_containment_failed} = ReceiverWorker.perform(oban_job)
+ end
+
+ test "canceled due to deleted object" do
+ params =
+ insert(:announce_activity).data
+ |> Map.put("object", "http://localhost:4001/deleted")
+
+ Tesla.Mock.mock(fn
+ %{url: "http://localhost:4001/deleted"} ->
+ %Tesla.Env{
+ status: 404,
+ body: ""
+ }
+ end)
+
+ {:ok, oban_job} =
+ Federator.incoming_ap_doc(%{
+ method: "POST",
+ req_headers: [],
+ request_path: "/inbox",
+ params: params,
+ query_string: ""
+ })
+
+ assert {:cancel, _} = ReceiverWorker.perform(oban_job)
+ end
end
diff --git a/test/pleroma/workers/remote_fetcher_worker_test.exs b/test/pleroma/workers/remote_fetcher_worker_test.exs
index 2104baab2..9caddb600 100644
--- a/test/pleroma/workers/remote_fetcher_worker_test.exs
+++ b/test/pleroma/workers/remote_fetcher_worker_test.exs
@@ -12,6 +12,7 @@ defmodule Pleroma.Workers.RemoteFetcherWorkerTest do
@deleted_object_two "https://deleted-410.example.com/"
@unauthorized_object "https://unauthorized.example.com/"
@depth_object "https://depth.example.com/"
+ @content_type_object "https://bad_content_type.example.com/"
describe "RemoteFetcherWorker" do
setup do
@@ -35,34 +36,48 @@ defmodule Pleroma.Workers.RemoteFetcherWorkerTest do
%Tesla.Env{
status: 200
}
+
+ %{method: :get, url: @content_type_object} ->
+ %Tesla.Env{
+ status: 200,
+ headers: [{"content-type", "application/json"}],
+ body: File.read!("test/fixtures/spoofed-object.json")
+ }
end)
end
- test "does not requeue a deleted object" do
- assert {:cancel, _} =
- RemoteFetcherWorker.perform(%Oban.Job{
- args: %{"op" => "fetch_remote", "id" => @deleted_object_one}
- })
+ test "does not retry jobs for a deleted object" do
+ [
+ %{"op" => "fetch_remote", "id" => @deleted_object_one},
+ %{"op" => "fetch_remote", "id" => @deleted_object_two}
+ ]
+ |> Enum.each(fn job -> assert {:cancel, _} = perform_job(RemoteFetcherWorker, job) end)
+ end
+ test "does not retry jobs for an unauthorized object" do
assert {:cancel, _} =
- RemoteFetcherWorker.perform(%Oban.Job{
- args: %{"op" => "fetch_remote", "id" => @deleted_object_two}
+ perform_job(RemoteFetcherWorker, %{
+ "op" => "fetch_remote",
+ "id" => @unauthorized_object
})
end
- test "does not requeue an unauthorized object" do
+ test "does not retry jobs for an an object that exceeded depth" do
+ clear_config([:instance, :federation_incoming_replies_max_depth], 0)
+
assert {:cancel, _} =
- RemoteFetcherWorker.perform(%Oban.Job{
- args: %{"op" => "fetch_remote", "id" => @unauthorized_object}
+ perform_job(RemoteFetcherWorker, %{
+ "op" => "fetch_remote",
+ "id" => @depth_object,
+ "depth" => 1
})
end
- test "does not requeue an object that exceeded depth" do
- clear_config([:instance, :federation_incoming_replies_max_depth], 0)
-
+ test "does not retry jobs for when object returns wrong content type" do
assert {:cancel, _} =
- RemoteFetcherWorker.perform(%Oban.Job{
- args: %{"op" => "fetch_remote", "id" => @depth_object, "depth" => 1}
+ perform_job(RemoteFetcherWorker, %{
+ "op" => "fetch_remote",
+ "id" => @content_type_object
})
end
end