Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into alexgleason/pleroma-confirm-users
This commit is contained in:
commit
fee0c6a2cb
|
@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- **Breaking:** Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
|
- **Breaking:** Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
|
||||||
- Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries.
|
- Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries.
|
||||||
- Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators.
|
- Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators.
|
||||||
|
- Admin API: Reports now ordered by newest
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mix tasks to help with displaying and removing ConfigDB entries. See `mix pleroma.config`.
|
- Mix tasks to help with displaying and removing ConfigDB entries. See `mix pleroma.config`.
|
||||||
- OAuth form improvements: users are remembered by their cookie, the CSS is overridable by the admin, and the style has been improved.
|
- OAuth form improvements: users are remembered by their cookie, the CSS is overridable by the admin, and the style has been improved.
|
||||||
- OAuth improvements and fixes: more secure session-based authentication (by token that could be revoked anytime), ability to revoke belonging OAuth token from any client etc.
|
- OAuth improvements and fixes: more secure session-based authentication (by token that could be revoked anytime), ability to revoke belonging OAuth token from any client etc.
|
||||||
|
- Ability to set ActivityPub aliases for follower migration.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
|
|
@ -134,6 +134,10 @@
|
||||||
|
|
||||||
config :pleroma, :cachex, provider: Pleroma.CachexMock
|
config :pleroma, :cachex, provider: Pleroma.CachexMock
|
||||||
|
|
||||||
|
config :pleroma, :side_effects,
|
||||||
|
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||||
|
logger: Pleroma.LoggerMock
|
||||||
|
|
||||||
if File.exists?("./config/test.secret.exs") do
|
if File.exists?("./config/test.secret.exs") do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -1123,6 +1123,7 @@ Loads json generated from `config/descriptions.exs`.
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"id": 1234,
|
||||||
"data": {
|
"data": {
|
||||||
"actor": {
|
"actor": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
|
|
@ -206,6 +206,7 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
||||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||||
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
||||||
|
- `also_known_as` - array of ActivityPub IDs, needed for following move
|
||||||
- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
|
- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
|
||||||
- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results).
|
- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results).
|
||||||
- `actor_type` - the type of this account.
|
- `actor_type` - the type of this account.
|
||||||
|
|
7
lib/pleroma/logging.ex
Normal file
7
lib/pleroma/logging.ex
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Logging do
|
||||||
|
@callback error(String.t()) :: any()
|
||||||
|
end
|
|
@ -142,7 +142,7 @@ defmodule Pleroma.User do
|
||||||
field(:allow_following_move, :boolean, default: true)
|
field(:allow_following_move, :boolean, default: true)
|
||||||
field(:skip_thread_containment, :boolean, default: false)
|
field(:skip_thread_containment, :boolean, default: false)
|
||||||
field(:actor_type, :string, default: "Person")
|
field(:actor_type, :string, default: "Person")
|
||||||
field(:also_known_as, {:array, :string}, default: [])
|
field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
field(:inbox, :string)
|
field(:inbox, :string)
|
||||||
field(:shared_inbox, :string)
|
field(:shared_inbox, :string)
|
||||||
field(:accepts_chat_messages, :boolean, default: nil)
|
field(:accepts_chat_messages, :boolean, default: nil)
|
||||||
|
@ -515,6 +515,7 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
:hide_follows_count,
|
:hide_follows_count,
|
||||||
:hide_favorites,
|
:hide_favorites,
|
||||||
:allow_following_move,
|
:allow_following_move,
|
||||||
|
:also_known_as,
|
||||||
:background,
|
:background,
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
|
@ -523,7 +524,6 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
:pleroma_settings_store,
|
:pleroma_settings_store,
|
||||||
:is_discoverable,
|
:is_discoverable,
|
||||||
:actor_type,
|
:actor_type,
|
||||||
:also_known_as,
|
|
||||||
:accepts_chat_messages
|
:accepts_chat_messages
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,6 +33,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.ActivityPub.Persisting
|
@behaviour Pleroma.Web.ActivityPub.ActivityPub.Persisting
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.ActivityPub.Streaming
|
||||||
|
|
||||||
defp get_recipients(%{"type" => "Create"} = data) do
|
defp get_recipients(%{"type" => "Create"} = data) do
|
||||||
to = Map.get(data, "to", [])
|
to = Map.get(data, "to", [])
|
||||||
|
@ -224,6 +225,7 @@ def stream_out_participations(participations) do
|
||||||
Streamer.stream("participation", participations)
|
Streamer.stream("participation", participations)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
def stream_out_participations(%Object{data: %{"context" => context}}, user) do
|
def stream_out_participations(%Object{data: %{"context" => context}}, user) do
|
||||||
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
|
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
|
||||||
conversation = Repo.preload(conversation, :participations)
|
conversation = Repo.preload(conversation, :participations)
|
||||||
|
@ -240,8 +242,10 @@ def stream_out_participations(%Object{data: %{"context" => context}}, user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
def stream_out_participations(_, _), do: :noop
|
def stream_out_participations(_, _), do: :noop
|
||||||
|
|
||||||
|
@impl true
|
||||||
def stream_out(%Activity{data: %{"type" => data_type}} = activity)
|
def stream_out(%Activity{data: %{"type" => data_type}} = activity)
|
||||||
when data_type in ["Create", "Announce", "Delete"] do
|
when data_type in ["Create", "Announce", "Delete"] do
|
||||||
activity
|
activity
|
||||||
|
@ -249,6 +253,7 @@ def stream_out(%Activity{data: %{"type" => data_type}} = activity)
|
||||||
|> Streamer.stream(activity)
|
|> Streamer.stream(activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
def stream_out(_activity) do
|
def stream_out(_activity) do
|
||||||
:noop
|
:noop
|
||||||
end
|
end
|
||||||
|
|
12
lib/pleroma/web/activity_pub/activity_pub/streaming.ex
Normal file
12
lib/pleroma/web/activity_pub/activity_pub/streaming.ex
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@callback stream_out(Activity.t()) :: any()
|
||||||
|
@callback stream_out_participations(Object.t(), User.t()) :: any()
|
||||||
|
end
|
|
@ -28,6 +28,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
|
@ap_streamer Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
|
||||||
|
@logger Pleroma.Config.get([:side_effects, :logger], Logger)
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
|
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
|
||||||
|
|
||||||
|
@ -287,12 +289,12 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object,
|
||||||
|
|
||||||
MessageReference.delete_for_object(deleted_object)
|
MessageReference.delete_for_object(deleted_object)
|
||||||
|
|
||||||
ActivityPub.stream_out(object)
|
@ap_streamer.stream_out(object)
|
||||||
ActivityPub.stream_out_participations(deleted_object, user)
|
@ap_streamer.stream_out_participations(deleted_object, user)
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:actor, _} ->
|
{:actor, _} ->
|
||||||
Logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
|
@logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
|
||||||
:no_object_actor
|
:no_object_actor
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,8 @@ def render("user.json", %{user: user}) do
|
||||||
"tag" => emoji_tags,
|
"tag" => emoji_tags,
|
||||||
# Note: key name is indeed "discoverable" (not an error)
|
# Note: key name is indeed "discoverable" (not an error)
|
||||||
"discoverable" => user.is_discoverable,
|
"discoverable" => user.is_discoverable,
|
||||||
"capabilities" => capabilities
|
"capabilities" => capabilities,
|
||||||
|
"alsoKnownAs" => user.also_known_as
|
||||||
}
|
}
|
||||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||||
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
|
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
|
||||||
|
|
|
@ -21,6 +21,7 @@ def render("show.json", %{log_entry: log_entry}) do
|
||||||
|> DateTime.to_unix()
|
|> DateTime.to_unix()
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
id: log_entry.id,
|
||||||
data: log_entry.data,
|
data: log_entry.data,
|
||||||
time: time,
|
time: time,
|
||||||
message: ModerationLog.get_log_entry_message(log_entry)
|
message: ModerationLog.get_log_entry_message(log_entry)
|
||||||
|
|
|
@ -19,8 +19,7 @@ def render("index.json", %{reports: reports}) do
|
||||||
reports:
|
reports:
|
||||||
reports[:items]
|
reports[:items]
|
||||||
|> Enum.map(&Report.extract_report_info/1)
|
|> Enum.map(&Report.extract_report_info/1)
|
||||||
|> Enum.map(&render(__MODULE__, "show.json", &1))
|
|> Enum.map(&render(__MODULE__, "show.json", &1)),
|
||||||
|> Enum.reverse(),
|
|
||||||
total: reports[:total]
|
total: reports[:total]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -614,6 +614,12 @@ defp update_credentials_request do
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Allows automatically follow moved following accounts"
|
description: "Allows automatically follow moved following accounts"
|
||||||
},
|
},
|
||||||
|
also_known_as: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string},
|
||||||
|
nullable: true,
|
||||||
|
description: "List of alternate ActivityPub IDs"
|
||||||
|
},
|
||||||
pleroma_background_image: %Schema{
|
pleroma_background_image: %Schema{
|
||||||
type: :string,
|
type: :string,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
@ -644,6 +650,7 @@ defp update_credentials_request do
|
||||||
pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
|
pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
|
||||||
skip_thread_containment: false,
|
skip_thread_containment: false,
|
||||||
allow_following_move: false,
|
allow_following_move: false,
|
||||||
|
also_known_as: ["https://foo.bar/users/foo"],
|
||||||
discoverable: false,
|
discoverable: false,
|
||||||
actor_type: "Person"
|
actor_type: "Person"
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
pleroma: %Schema{
|
pleroma: %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
|
ap_id: %Schema{type: :string},
|
||||||
|
also_known_as: %Schema{type: :array, items: %Schema{type: :string}},
|
||||||
allow_following_move: %Schema{
|
allow_following_move: %Schema{
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "whether the user allows automatically follow moved following accounts"
|
description: "whether the user allows automatically follow moved following accounts"
|
||||||
|
|
|
@ -184,6 +184,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
:allow_following_move,
|
:allow_following_move,
|
||||||
|
:also_known_as,
|
||||||
:accepts_chat_messages
|
:accepts_chat_messages
|
||||||
]
|
]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
|
@ -207,6 +208,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
if bot, do: {:ok, "Service"}, else: {:ok, "Person"}
|
if bot, do: {:ok, "Service"}, else: {:ok, "Person"}
|
||||||
end)
|
end)
|
||||||
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
||||||
|
|> Maps.put_if_present(:also_known_as, params[:also_known_as])
|
||||||
# Note: param name is indeed :locked (not an error)
|
# Note: param name is indeed :locked (not an error)
|
||||||
|> Maps.put_if_present(:is_locked, params[:locked])
|
|> Maps.put_if_present(:is_locked, params[:locked])
|
||||||
# Note: param name is indeed :discoverable (not an error)
|
# Note: param name is indeed :discoverable (not an error)
|
||||||
|
|
|
@ -265,6 +265,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
# Pleroma extension
|
# Pleroma extension
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
|
also_known_as: user.also_known_as,
|
||||||
confirmation_pending: user.confirmation_pending,
|
confirmation_pending: user.confirmation_pending,
|
||||||
tags: user.tags,
|
tags: user.tags,
|
||||||
hide_followers_count: user.hide_followers_count,
|
hide_followers_count: user.hide_followers_count,
|
||||||
|
|
|
@ -58,12 +58,16 @@ defp gather_links(%User{} = user) do
|
||||||
] ++ Publisher.gather_webfinger_links(user)
|
] ++ Publisher.gather_webfinger_links(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp gather_aliases(%User{} = user) do
|
||||||
|
[user.ap_id | user.also_known_as]
|
||||||
|
end
|
||||||
|
|
||||||
def represent_user(user, "JSON") do
|
def represent_user(user, "JSON") do
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
{:ok, user} = User.ensure_keys_present(user)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
|
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
|
||||||
"aliases" => [user.ap_id],
|
"aliases" => gather_aliases(user),
|
||||||
"links" => gather_links(user)
|
"links" => gather_links(user)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -71,6 +75,11 @@ def represent_user(user, "JSON") do
|
||||||
def represent_user(user, "XML") do
|
def represent_user(user, "XML") do
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
{:ok, user} = User.ensure_keys_present(user)
|
||||||
|
|
||||||
|
aliases =
|
||||||
|
user
|
||||||
|
|> gather_aliases()
|
||||||
|
|> Enum.map(&{:Alias, &1})
|
||||||
|
|
||||||
links =
|
links =
|
||||||
gather_links(user)
|
gather_links(user)
|
||||||
|> Enum.map(fn link -> {:Link, link} end)
|
|> Enum.map(fn link -> {:Link, link} end)
|
||||||
|
@ -79,9 +88,8 @@ def represent_user(user, "XML") do
|
||||||
:XRD,
|
:XRD,
|
||||||
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
|
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
|
||||||
[
|
[
|
||||||
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"},
|
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}
|
||||||
{:Alias, user.ap_id}
|
] ++ aliases ++ links
|
||||||
] ++ links
|
|
||||||
}
|
}
|
||||||
|> XmlBuilder.to_doc()
|
|> XmlBuilder.to_doc()
|
||||||
end
|
end
|
||||||
|
|
147
test/pleroma/web/activity_pub/side_effects/delete_test.exs
Normal file
147
test/pleroma/web/activity_pub/side_effects/delete_test.exs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.SideEffects.DeleteTest do
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
|
alias Pleroma.Web.ActivityPub.SideEffects
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
alias Pleroma.LoggerMock
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPubMock
|
||||||
|
|
||||||
|
import Mox
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "user deletion" do
|
||||||
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
|
||||||
|
{:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
|
||||||
|
|
||||||
|
%{
|
||||||
|
user: user,
|
||||||
|
delete_user: delete_user
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it handles user deletions", %{delete_user: delete, user: user} do
|
||||||
|
{:ok, _delete, _} = SideEffects.handle(delete)
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert User.get_cached_by_ap_id(user.ap_id).deactivated
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "object deletion" do
|
||||||
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
|
||||||
|
{:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
|
||||||
|
{:ok, favorite} = CommonAPI.favorite(user, post.id)
|
||||||
|
object = Object.normalize(post)
|
||||||
|
{:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
|
||||||
|
{:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
|
||||||
|
|
||||||
|
%{
|
||||||
|
user: user,
|
||||||
|
delete: delete,
|
||||||
|
post: post,
|
||||||
|
object: object,
|
||||||
|
op: op,
|
||||||
|
favorite: favorite
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it handles object deletions", %{
|
||||||
|
delete: delete,
|
||||||
|
post: post,
|
||||||
|
object: object,
|
||||||
|
user: user,
|
||||||
|
op: op,
|
||||||
|
favorite: favorite
|
||||||
|
} do
|
||||||
|
object_id = object.id
|
||||||
|
user_id = user.id
|
||||||
|
|
||||||
|
ActivityPubMock
|
||||||
|
|> expect(:stream_out, fn ^delete -> nil end)
|
||||||
|
|> expect(:stream_out_participations, fn %Object{id: ^object_id}, %User{id: ^user_id} ->
|
||||||
|
nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, _delete, _} = SideEffects.handle(delete)
|
||||||
|
user = User.get_cached_by_ap_id(object.data["actor"])
|
||||||
|
|
||||||
|
object = Object.get_by_id(object.id)
|
||||||
|
assert object.data["type"] == "Tombstone"
|
||||||
|
refute Activity.get_by_id(post.id)
|
||||||
|
refute Activity.get_by_id(favorite.id)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
assert user.note_count == 0
|
||||||
|
|
||||||
|
object = Object.normalize(op.data["object"], false)
|
||||||
|
|
||||||
|
assert object.data["repliesCount"] == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it handles object deletions when the object itself has been pruned", %{
|
||||||
|
delete: delete,
|
||||||
|
post: post,
|
||||||
|
object: object,
|
||||||
|
user: user,
|
||||||
|
op: op
|
||||||
|
} do
|
||||||
|
object_id = object.id
|
||||||
|
user_id = user.id
|
||||||
|
|
||||||
|
ActivityPubMock
|
||||||
|
|> expect(:stream_out, fn ^delete -> nil end)
|
||||||
|
|> expect(:stream_out_participations, fn %Object{id: ^object_id}, %User{id: ^user_id} ->
|
||||||
|
nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, _delete, _} = SideEffects.handle(delete)
|
||||||
|
user = User.get_cached_by_ap_id(object.data["actor"])
|
||||||
|
|
||||||
|
object = Object.get_by_id(object.id)
|
||||||
|
assert object.data["type"] == "Tombstone"
|
||||||
|
refute Activity.get_by_id(post.id)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
assert user.note_count == 0
|
||||||
|
|
||||||
|
object = Object.normalize(op.data["object"], false)
|
||||||
|
|
||||||
|
assert object.data["repliesCount"] == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it logs issues with objects deletion", %{
|
||||||
|
delete: delete,
|
||||||
|
object: object
|
||||||
|
} do
|
||||||
|
{:ok, _object} =
|
||||||
|
object
|
||||||
|
|> Object.change(%{data: Map.delete(object.data, "actor")})
|
||||||
|
|> Repo.update()
|
||||||
|
|
||||||
|
LoggerMock
|
||||||
|
|> expect(:error, fn str -> assert str =~ "The object doesn't have an actor" end)
|
||||||
|
|
||||||
|
{:error, :no_object_actor} = SideEffects.handle(delete)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,7 +19,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
||||||
alias Pleroma.Web.ActivityPub.SideEffects
|
alias Pleroma.Web.ActivityPub.SideEffects
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import ExUnit.CaptureLog
|
|
||||||
import Mock
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
@ -131,115 +130,6 @@ test "it uses a given changeset to update", %{user: user, update: update} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delete objects" do
|
|
||||||
setup do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
|
|
||||||
{:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
|
|
||||||
{:ok, favorite} = CommonAPI.favorite(user, post.id)
|
|
||||||
object = Object.normalize(post)
|
|
||||||
{:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
|
|
||||||
{:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
|
|
||||||
{:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
|
|
||||||
{:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
|
|
||||||
|
|
||||||
%{
|
|
||||||
user: user,
|
|
||||||
delete: delete,
|
|
||||||
post: post,
|
|
||||||
object: object,
|
|
||||||
delete_user: delete_user,
|
|
||||||
op: op,
|
|
||||||
favorite: favorite
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it handles object deletions", %{
|
|
||||||
delete: delete,
|
|
||||||
post: post,
|
|
||||||
object: object,
|
|
||||||
user: user,
|
|
||||||
op: op,
|
|
||||||
favorite: favorite
|
|
||||||
} do
|
|
||||||
with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
|
|
||||||
stream_out: fn _ -> nil end,
|
|
||||||
stream_out_participations: fn _, _ -> nil end do
|
|
||||||
{:ok, delete, _} = SideEffects.handle(delete)
|
|
||||||
user = User.get_cached_by_ap_id(object.data["actor"])
|
|
||||||
|
|
||||||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
|
|
||||||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
|
|
||||||
end
|
|
||||||
|
|
||||||
object = Object.get_by_id(object.id)
|
|
||||||
assert object.data["type"] == "Tombstone"
|
|
||||||
refute Activity.get_by_id(post.id)
|
|
||||||
refute Activity.get_by_id(favorite.id)
|
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
|
||||||
assert user.note_count == 0
|
|
||||||
|
|
||||||
object = Object.normalize(op.data["object"], false)
|
|
||||||
|
|
||||||
assert object.data["repliesCount"] == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it handles object deletions when the object itself has been pruned", %{
|
|
||||||
delete: delete,
|
|
||||||
post: post,
|
|
||||||
object: object,
|
|
||||||
user: user,
|
|
||||||
op: op
|
|
||||||
} do
|
|
||||||
with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
|
|
||||||
stream_out: fn _ -> nil end,
|
|
||||||
stream_out_participations: fn _, _ -> nil end do
|
|
||||||
{:ok, delete, _} = SideEffects.handle(delete)
|
|
||||||
user = User.get_cached_by_ap_id(object.data["actor"])
|
|
||||||
|
|
||||||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
|
|
||||||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
|
|
||||||
end
|
|
||||||
|
|
||||||
object = Object.get_by_id(object.id)
|
|
||||||
assert object.data["type"] == "Tombstone"
|
|
||||||
refute Activity.get_by_id(post.id)
|
|
||||||
|
|
||||||
user = User.get_by_id(user.id)
|
|
||||||
assert user.note_count == 0
|
|
||||||
|
|
||||||
object = Object.normalize(op.data["object"], false)
|
|
||||||
|
|
||||||
assert object.data["repliesCount"] == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it handles user deletions", %{delete_user: delete, user: user} do
|
|
||||||
{:ok, _delete, _} = SideEffects.handle(delete)
|
|
||||||
ObanHelpers.perform_all()
|
|
||||||
|
|
||||||
assert User.get_cached_by_ap_id(user.ap_id).deactivated
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it logs issues with objects deletion", %{
|
|
||||||
delete: delete,
|
|
||||||
object: object
|
|
||||||
} do
|
|
||||||
{:ok, object} =
|
|
||||||
object
|
|
||||||
|> Object.change(%{data: Map.delete(object.data, "actor")})
|
|
||||||
|> Repo.update()
|
|
||||||
|
|
||||||
Object.invalid_object_cache(object)
|
|
||||||
|
|
||||||
assert capture_log(fn ->
|
|
||||||
{:error, :no_object_actor} = SideEffects.handle(delete)
|
|
||||||
end) =~ "object doesn't have an actor"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "EmojiReact objects" do
|
describe "EmojiReact objects" do
|
||||||
setup do
|
setup do
|
||||||
poster = insert(:user)
|
poster = insert(:user)
|
||||||
|
|
|
@ -80,6 +80,12 @@ test "renders an invisible user with the invisible property set to true" do
|
||||||
assert %{"invisible" => true} = UserView.render("service.json", %{user: user})
|
assert %{"invisible" => true} = UserView.render("service.json", %{user: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "renders AKAs" do
|
||||||
|
akas = ["https://i.tusooa.xyz/users/test-pleroma"]
|
||||||
|
user = insert(:user, also_known_as: akas)
|
||||||
|
assert %{"alsoKnownAs" => ^akas} = UserView.render("user.json", %{user: user})
|
||||||
|
end
|
||||||
|
|
||||||
describe "endpoints" do
|
describe "endpoints" do
|
||||||
test "local users have a usable endpoints structure" do
|
test "local users have a usable endpoints structure" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.AdminAPI.ModerationLogViewTest do
|
||||||
describe "renders `report_note_delete` log messages" do
|
describe "renders `report_note_delete` log messages" do
|
||||||
setup do
|
setup do
|
||||||
log1 = %Pleroma.ModerationLog{
|
log1 = %Pleroma.ModerationLog{
|
||||||
|
id: 1,
|
||||||
data: %{
|
data: %{
|
||||||
"action" => "report_note_delete",
|
"action" => "report_note_delete",
|
||||||
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
||||||
|
@ -21,6 +22,7 @@ defmodule Pleroma.Web.AdminAPI.ModerationLogViewTest do
|
||||||
}
|
}
|
||||||
|
|
||||||
log2 = %Pleroma.ModerationLog{
|
log2 = %Pleroma.ModerationLog{
|
||||||
|
id: 2,
|
||||||
data: %{
|
data: %{
|
||||||
"action" => "report_note_delete",
|
"action" => "report_note_delete",
|
||||||
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
||||||
|
@ -42,6 +44,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do
|
||||||
) == %{
|
) == %{
|
||||||
items: [
|
items: [
|
||||||
%{
|
%{
|
||||||
|
id: 1,
|
||||||
data: %{
|
data: %{
|
||||||
"action" => "report_note_delete",
|
"action" => "report_note_delete",
|
||||||
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
||||||
|
@ -59,6 +62,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do
|
||||||
time: 1_605_622_400
|
time: 1_605_622_400
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
id: 2,
|
||||||
data: %{
|
data: %{
|
||||||
"action" => "report_note_delete",
|
"action" => "report_note_delete",
|
||||||
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
||||||
|
@ -82,6 +86,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do
|
||||||
|
|
||||||
test "renders `report_note_delete` log message", %{log1: log} do
|
test "renders `report_note_delete` log message", %{log1: log} do
|
||||||
assert ModerationLogView.render("show.json", %{log_entry: log}) == %{
|
assert ModerationLogView.render("show.json", %{log_entry: log}) == %{
|
||||||
|
id: 1,
|
||||||
data: %{
|
data: %{
|
||||||
"action" => "report_note_delete",
|
"action" => "report_note_delete",
|
||||||
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
|
||||||
|
|
|
@ -143,4 +143,29 @@ test "doesn't error out when the user doesn't exists" do
|
||||||
|
|
||||||
assert %{} = ReportView.render("show.json", Report.extract_report_info(activity))
|
assert %{} = ReportView.render("show.json", Report.extract_report_info(activity))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "reports are ordered newest first" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, report1} =
|
||||||
|
CommonAPI.report(user, %{
|
||||||
|
account_id: other_user.id,
|
||||||
|
comment: "first report"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, report2} =
|
||||||
|
CommonAPI.report(user, %{
|
||||||
|
account_id: other_user.id,
|
||||||
|
comment: "second report"
|
||||||
|
})
|
||||||
|
|
||||||
|
%{reports: rendered} =
|
||||||
|
ReportView.render("index.json",
|
||||||
|
reports: Pleroma.Web.ActivityPub.Utils.get_reports(%{}, 1, 50)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert report2.id == rendered |> Enum.at(0) |> Map.get(:id)
|
||||||
|
assert report1.id == rendered |> Enum.at(1) |> Map.get(:id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -218,6 +218,25 @@ test "updates the user's name", %{conn: conn} do
|
||||||
assert update_activity.data["object"]["name"] == "markorepairs"
|
assert update_activity.data["object"]["name"] == "markorepairs"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updates the user's AKAs", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
patch(conn, "/api/v1/accounts/update_credentials", %{
|
||||||
|
"also_known_as" => ["https://mushroom.kingdom/users/mario"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||||
|
assert user_data["pleroma"]["also_known_as"] == ["https://mushroom.kingdom/users/mario"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't update non-url akas", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
patch(conn, "/api/v1/accounts/update_credentials", %{
|
||||||
|
"also_known_as" => ["aReallyCoolGuy"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, 403)
|
||||||
|
end
|
||||||
|
|
||||||
test "updates the user's avatar", %{user: user, conn: conn} do
|
test "updates the user's avatar", %{user: user, conn: conn} do
|
||||||
new_avatar = %Plug.Upload{
|
new_avatar = %Plug.Upload{
|
||||||
content_type: "image/jpeg",
|
content_type: "image/jpeg",
|
||||||
|
|
|
@ -35,7 +35,8 @@ test "Represent a user account" do
|
||||||
"<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"",
|
"<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"",
|
||||||
inserted_at: ~N[2017-08-15 15:47:06.597036],
|
inserted_at: ~N[2017-08-15 15:47:06.597036],
|
||||||
emoji: %{"karjalanpiirakka" => "/file.png"},
|
emoji: %{"karjalanpiirakka" => "/file.png"},
|
||||||
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\""
|
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"",
|
||||||
|
also_known_as: ["https://shitposter.zone/users/shp"]
|
||||||
})
|
})
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
|
@ -75,6 +76,7 @@ test "Represent a user account" do
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
|
also_known_as: ["https://shitposter.zone/users/shp"],
|
||||||
background_image: "https://example.com/images/asuka_hospital.png",
|
background_image: "https://example.com/images/asuka_hospital.png",
|
||||||
favicon: nil,
|
favicon: nil,
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
|
@ -173,6 +175,7 @@ test "Represent a Service(bot) account" do
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
|
also_known_as: [],
|
||||||
background_image: nil,
|
background_image: nil,
|
||||||
favicon: nil,
|
favicon: nil,
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
|
|
|
@ -30,14 +30,24 @@ test "GET host-meta" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Webfinger JRD" do
|
test "Webfinger JRD" do
|
||||||
user = insert(:user)
|
user =
|
||||||
|
insert(:user,
|
||||||
|
ap_id: "https://hyrule.world/users/zelda",
|
||||||
|
also_known_as: ["https://mushroom.kingdom/users/toad"]
|
||||||
|
)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> put_req_header("accept", "application/jrd+json")
|
|> put_req_header("accept", "application/jrd+json")
|
||||||
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost"
|
assert response["subject"] == "acct:#{user.nickname}@localhost"
|
||||||
|
|
||||||
|
assert response["aliases"] == [
|
||||||
|
"https://hyrule.world/users/zelda",
|
||||||
|
"https://mushroom.kingdom/users/toad"
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns 404 when user isn't found (JSON)" do
|
test "it returns 404 when user isn't found (JSON)" do
|
||||||
|
@ -51,14 +61,20 @@ test "it returns 404 when user isn't found (JSON)" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Webfinger XML" do
|
test "Webfinger XML" do
|
||||||
user = insert(:user)
|
user =
|
||||||
|
insert(:user,
|
||||||
|
ap_id: "https://hyrule.world/users/zelda",
|
||||||
|
also_known_as: ["https://mushroom.kingdom/users/toad"]
|
||||||
|
)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> put_req_header("accept", "application/xrd+xml")
|
|> put_req_header("accept", "application/xrd+xml")
|
||||||
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|
||||||
|
|> response(200)
|
||||||
|
|
||||||
assert response(response, 200)
|
assert response =~ "<Alias>https://hyrule.world/users/zelda</Alias>"
|
||||||
|
assert response =~ "<Alias>https://mushroom.kingdom/users/toad</Alias>"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns 404 when user isn't found (XML)" do
|
test "it returns 404 when user isn't found (XML)" do
|
||||||
|
|
|
@ -138,6 +138,8 @@ defp json_response_and_validate_schema(conn, _status) do
|
||||||
|
|
||||||
Pleroma.DataCase.stub_pipeline()
|
Pleroma.DataCase.stub_pipeline()
|
||||||
|
|
||||||
|
Mox.verify_on_exit!()
|
||||||
|
|
||||||
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -85,6 +85,8 @@ def clear_cachex do
|
||||||
|
|
||||||
stub_pipeline()
|
stub_pipeline()
|
||||||
|
|
||||||
|
Mox.verify_on_exit!()
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,10 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
Mox.defmock(Pleroma.Web.ActivityPub.ActivityPubMock,
|
Mox.defmock(Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||||
for: Pleroma.Web.ActivityPub.ActivityPub.Persisting
|
for: [
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPub.Persisting,
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPub.Streaming
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
Mox.defmock(Pleroma.Web.ActivityPub.SideEffectsMock,
|
Mox.defmock(Pleroma.Web.ActivityPub.SideEffectsMock,
|
||||||
|
@ -23,3 +26,5 @@
|
||||||
Mox.defmock(Pleroma.Web.FederatorMock, for: Pleroma.Web.Federator.Publishing)
|
Mox.defmock(Pleroma.Web.FederatorMock, for: Pleroma.Web.Federator.Publishing)
|
||||||
|
|
||||||
Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting)
|
Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting)
|
||||||
|
|
||||||
|
Mox.defmock(Pleroma.LoggerMock, for: Pleroma.Logging)
|
||||||
|
|
Loading…
Reference in a new issue