Page MenuHomePhorge

No OneTemporary

Size
16 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/pleroma/web/activity_pub/importer.ex b/lib/pleroma/web/activity_pub/importer.ex
index d7ced3d8b..e8adf8250 100644
--- a/lib/pleroma/web/activity_pub/importer.ex
+++ b/lib/pleroma/web/activity_pub/importer.ex
@@ -1,85 +1,90 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Importer do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
require Pleroma.Constants
def import_object(
%{
"type" => type,
"published" => published,
"context" => context
} = object,
%User{} = user,
opts
)
when type in Pleroma.Constants.status_types() do
fixed_object =
object
|> strip_ap_id()
|> rewrite_actor(user)
|> strip_recipients(opts)
create_data =
- Utils.make_create_data(%{
- actor: user,
- published: published,
- object: fixed_object,
- to: [],
- context: context
- }, %{})
+ Utils.make_create_data(
+ %{
+ actor: user,
+ published: published,
+ object: fixed_object,
+ to: [],
+ context: context
+ },
+ %{}
+ )
|> Utils.lazy_put_activity_defaults()
- with {:ok, activity, _meta} <- Pipeline.common_pipeline(create_data, local: true, importing: true) do
+ with {:ok, activity, _meta} <-
+ Pipeline.common_pipeline(create_data, local: true, importing: true) do
# This is to meant to be executed in the oban queue, we only care if it succeeds,
# so don't query for and put in the object here.
{:ok, activity}
else
e -> e
end
end
def import_activity(%{"type" => "Create", "object" => object} = _activity, %User{} = user, opts) do
import_object(object, user, opts)
end
def import_activity(_, _, _), do: nil
def import_one(%{"type" => type} = object, %User{} = user, opts \\ []) do
if type in Pleroma.Constants.status_types() do
import_object(object, user, opts)
else
import_activity(object, user, opts)
end
end
defp strip_ap_id(object), do: object |> Map.drop(["id"])
defp rewrite_actor(object, user) do
object
|> Map.put("actor", user.ap_id)
end
defp strip_recipients(object, opts) do
keep_unlisted = opts[:keep_unlisted] || false
orig_is_public = Visibility.is_public?(object) and not Visibility.is_local_public?(object)
+
unlisted_ccs =
if keep_unlisted and orig_is_public do
[Pleroma.Constants.as_public()]
else
[]
end
object
|> Map.put("to", [])
|> Map.put("cc", [object["actor"] | unlisted_ccs])
|> Map.put("bto", [])
|> Map.put("bcc", [])
end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex
index 5f5bcf700..298bd5963 100644
--- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex
@@ -1,87 +1,93 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do
{:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback)
data =
Enum.reject(data, fn x ->
String.ends_with?(x, "/followers") and x != follower_collection
end)
Map.put(message, field, data)
end
def dont_apply_when_importing(data, func, meta) do
if meta[:importing] do
data
else
func.(data)
end
end
def fix_object_defaults(data, meta) do
context =
Utils.maybe_create_context(
data["context"] || data["conversation"] || data["inReplyTo"] || data["id"]
)
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"])
data
|> Map.put("context", context)
|> cast_and_filter_recipients("to", follower_collection)
|> cast_and_filter_recipients("cc", follower_collection)
|> cast_and_filter_recipients("bto", follower_collection)
|> cast_and_filter_recipients("bcc", follower_collection)
- |> dont_apply_when_importing(& Transmogrifier.fix_implicit_addressing(&1, follower_collection), meta)
+ |> dont_apply_when_importing(
+ &Transmogrifier.fix_implicit_addressing(&1, follower_collection),
+ meta
+ )
end
def fix_activity_addressing(activity, meta \\ []) do
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"])
activity
|> cast_and_filter_recipients("to", follower_collection)
|> cast_and_filter_recipients("cc", follower_collection)
|> cast_and_filter_recipients("bto", follower_collection)
|> cast_and_filter_recipients("bcc", follower_collection)
- |> dont_apply_when_importing(& Transmogrifier.fix_implicit_addressing(&1, follower_collection), meta)
+ |> dont_apply_when_importing(
+ &Transmogrifier.fix_implicit_addressing(&1, follower_collection),
+ meta
+ )
end
def fix_actor(data) do
actor =
data
|> Map.put_new("actor", data["attributedTo"])
|> Containment.get_actor()
data
|> Map.put("actor", actor)
|> Map.put("attributedTo", actor)
end
def fix_activity_context(data, %Object{data: %{"context" => object_context}}) do
data
|> Map.put("context", object_context)
end
def fix_object_action_recipients(%{"actor" => actor} = data, %Object{data: %{"actor" => actor}}) do
to = ((data["to"] || []) -- [actor]) |> Enum.uniq()
Map.put(data, "to", to)
end
def fix_object_action_recipients(data, %Object{data: %{"actor" => actor}}) do
to = ((data["to"] || []) ++ [actor]) |> Enum.uniq()
Map.put(data, "to", to)
end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex
index 099521763..201abe3db 100644
--- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex
@@ -1,161 +1,164 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
# Code based on CreateChatMessageValidator
# NOTES
# - doesn't embed, will only get the object id
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
use Ecto.Schema
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
alias Pleroma.Web.ActivityPub.Transmogrifier
import Ecto.Changeset
@primary_key false
embedded_schema do
quote do
unquote do
import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
message_fields()
activity_fields()
end
end
field(:expires_at, ObjectValidators.DateTime)
# Should be moved to object, done for CommonAPI.Utils.make_context
field(:context, :string)
end
def cast_data(data, meta \\ []) do
data = fix(data, meta)
%__MODULE__{}
|> changeset(data)
end
def cast_and_apply(data) do
data
|> cast_data
|> apply_action(:insert)
end
def cast_and_validate(data, meta \\ []) do
data
|> cast_data(meta)
|> validate_data(meta)
end
def changeset(struct, data) do
struct
|> cast(data, __schema__(:fields))
end
# CommonFixes.fix_activity_addressing adapted for Create specific behavior
defp fix_addressing(data, object, meta) do
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["actor"])
data
|> CommonFixes.cast_and_filter_recipients("to", follower_collection, object["to"])
|> CommonFixes.cast_and_filter_recipients("cc", follower_collection, object["cc"])
|> CommonFixes.cast_and_filter_recipients("bto", follower_collection, object["bto"])
|> CommonFixes.cast_and_filter_recipients("bcc", follower_collection, object["bcc"])
- |> CommonFixes.dont_apply_when_importing(& Transmogrifier.fix_implicit_addressing(&1, follower_collection), meta)
+ |> CommonFixes.dont_apply_when_importing(
+ &Transmogrifier.fix_implicit_addressing(&1, follower_collection),
+ meta
+ )
end
def fix(data, meta) do
object = meta[:object_data]
data
|> CommonFixes.fix_actor()
|> Map.put("context", object["context"])
|> fix_addressing(object, meta)
end
defp validate_data(cng, meta) do
object = meta[:object_data]
cng
|> validate_required([:actor, :type, :object, :to, :cc])
|> validate_inclusion(:type, ["Create"])
|> CommonValidations.validate_actor_presence()
|> validate_actors_match(object)
|> validate_context_match(object)
|> validate_addressing_match(object)
|> validate_object_nonexistence()
|> validate_object_containment()
end
def validate_object_containment(cng) do
actor = get_field(cng, :actor)
cng
|> validate_change(:object, fn :object, object_id ->
%URI{host: object_id_host} = URI.parse(object_id)
%URI{host: actor_host} = URI.parse(actor)
if object_id_host == actor_host do
[]
else
[{:object, "The host of the object id doesn't match with the host of the actor"}]
end
end)
end
def validate_object_nonexistence(cng) do
cng
|> validate_change(:object, fn :object, object_id ->
if Object.get_cached_by_ap_id(object_id) do
[{:object, "The object to create already exists"}]
else
[]
end
end)
end
def validate_actors_match(cng, object) do
attributed_to = object["attributedTo"] || object["actor"]
cng
|> validate_change(:actor, fn :actor, actor ->
if actor == attributed_to do
[]
else
[{:actor, "Actor doesn't match with object attributedTo"}]
end
end)
end
def validate_context_match(cng, %{"context" => object_context}) do
cng
|> validate_change(:context, fn :context, context ->
if context == object_context do
[]
else
[{:context, "context field not matching between Create and object (#{object_context})"}]
end
end)
end
def validate_addressing_match(cng, object) do
[:to, :cc, :bcc, :bto]
|> Enum.reduce(cng, fn field, cng ->
object_data = object[to_string(field)]
validate_change(cng, field, fn field, data ->
if data == object_data do
[]
else
[{field, "field doesn't match with object (#{inspect(object_data)})"}]
end
end)
end)
end
end
diff --git a/test/pleroma/web/activity_pub/importer_test.exs b/test/pleroma/web/activity_pub/importer_test.exs
index 93e92b01f..9f2423612 100644
--- a/test/pleroma/web/activity_pub/importer_test.exs
+++ b/test/pleroma/web/activity_pub/importer_test.exs
@@ -1,103 +1,112 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ImporterTest do
use Pleroma.DataCase
alias Pleroma.Activity
alias Pleroma.Web.ActivityPub.Importer
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI
require Pleroma.Constants
import Pleroma.Factory
describe "import_one/3" do
test "it imports an activity" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "mew"})
{:ok, activity_for_import} = Transmogrifier.prepare_outgoing(activity.data)
importing_user = insert(:user)
assert {:ok, imported_activity} = Importer.import_one(activity_for_import, importing_user)
imported_activity = Activity.normalize(imported_activity)
assert imported_activity.id != activity.id
assert imported_activity.actor == importing_user.ap_id
assert imported_activity.object.data["actor"] == importing_user.ap_id
assert imported_activity.object.data["content"] == activity.object.data["content"]
assert imported_activity.object.data["published"] == activity.object.data["published"]
end
test "it strips all mentions" do
user = insert(:user)
user2 = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "mew @#{user2.nickname}"})
assert user2.ap_id in activity.recipients
{:ok, activity_for_import} = Transmogrifier.prepare_outgoing(activity.data)
importing_user = insert(:user)
assert {:ok, imported_activity} = Importer.import_one(activity_for_import, importing_user)
imported_activity = Activity.normalize(imported_activity)
assert [importing_user.ap_id] == imported_activity.recipients
assert [] == imported_activity.object.data["to"]
assert [importing_user.ap_id] == imported_activity.object.data["cc"]
end
test "it keeps inReplyTo and context" do
user = insert(:user)
user2 = insert(:user)
{:ok, replied_to_activity} = CommonAPI.post(user2, %{status: "mew"})
- {:ok, activity} = CommonAPI.post(user, %{status: "mew @#{user2.nickname}", in_reply_to_id: replied_to_activity})
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status: "mew @#{user2.nickname}",
+ in_reply_to_id: replied_to_activity
+ })
+
assert user2.ap_id in activity.recipients
{:ok, activity_for_import} = Transmogrifier.prepare_outgoing(activity.data)
importing_user = insert(:user)
assert {:ok, imported_activity} = Importer.import_one(activity_for_import, importing_user)
imported_activity = Activity.normalize(imported_activity)
assert [importing_user.ap_id] == imported_activity.recipients
assert imported_activity.object.data["context"] == activity.object.data["context"]
assert imported_activity.object.data["inReplyTo"] == activity.object.data["inReplyTo"]
assert imported_activity.data["context"] == activity.data["context"]
end
test "it keeps public and unlisted posts unlisted with keep_unlisted option" do
verify_with_visibility = fn visibility, yn ->
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "mew", visibility: visibility})
{:ok, activity_for_import} = Transmogrifier.prepare_outgoing(activity.data)
importing_user = insert(:user)
- assert {:ok, imported_activity} = Importer.import_one(activity_for_import, importing_user, keep_unlisted: true)
+
+ assert {:ok, imported_activity} =
+ Importer.import_one(activity_for_import, importing_user, keep_unlisted: true)
+
imported_activity = Activity.normalize(imported_activity)
if yn do
assert [_, _] = imported_activity.recipients
else
assert [_] = imported_activity.recipients
end
assert importing_user.ap_id in imported_activity.recipients
assert Pleroma.Constants.as_public() in imported_activity.recipients == yn
assert Pleroma.Constants.as_public() in imported_activity.data["cc"] == yn
refute Pleroma.Constants.as_public() in imported_activity.data["to"]
assert Pleroma.Constants.as_public() in imported_activity.object.data["cc"] == yn
refute Pleroma.Constants.as_public() in imported_activity.object.data["to"]
end
verify_with_visibility.("public", true)
verify_with_visibility.("unlisted", true)
verify_with_visibility.("local", false)
verify_with_visibility.("private", false)
verify_with_visibility.("direct", false)
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Fri, Aug 28, 10:51 AM (4 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1736363
Default Alt Text
(16 KB)

Event Timeline