summaryrefslogtreecommitdiff
path: root/lib/pleroma/web/activity_pub
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/web/activity_pub')
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex14
-rw-r--r--lib/pleroma/web/activity_pub/builder.ex22
-rw-r--r--lib/pleroma/web/activity_pub/object_validator.ex32
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex84
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/common_validations.ex6
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex40
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex (renamed from lib/pleroma/web/activity_pub/object_validators/create_validator.ex)0
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/types/recipients.ex23
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex29
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex7
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier/chat_message_handling.ex33
11 files changed, 288 insertions, 2 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 86b105b7f..b6ba91052 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -397,6 +397,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ # TODO: Is this even used now?
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
@spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t(), Object.t()} | {:error, any()}
@@ -1206,6 +1207,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ defp exclude_chat_messages(query, %{"include_chat_messages" => true}), do: query
+
+ defp exclude_chat_messages(query, _) do
+ if has_named_binding?(query, :object) do
+ from([activity, object: o] in query,
+ where: fragment("not(?->>'type' = ?)", o.data, "ChatMessage")
+ )
+ else
+ query
+ end
+ end
+
defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
from(activity in query, where: activity.id != ^id)
end
@@ -1311,6 +1324,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_instance(opts)
|> Activity.restrict_deactivated_users()
|> exclude_poll_votes(opts)
+ |> exclude_chat_messages(opts)
|> exclude_visibility(opts)
end
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index 429a510b8..f0a6c1e1b 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -10,6 +10,28 @@ defmodule Pleroma.Web.ActivityPub.Builder do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ def create(actor, object_id, recipients) do
+ {:ok,
+ %{
+ "id" => Utils.generate_activity_id(),
+ "actor" => actor.ap_id,
+ "to" => recipients,
+ "object" => object_id,
+ "type" => "Create"
+ }, []}
+ end
+
+ def chat_message(actor, recipient, content) do
+ {:ok,
+ %{
+ "id" => Utils.generate_object_id(),
+ "actor" => actor.ap_id,
+ "type" => "ChatMessage",
+ "to" => [recipient],
+ "content" => content
+ }, []}
+ end
+
@spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
def like(actor, object) do
object_actor = User.get_cached_by_ap_id(object.data["actor"])
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index dc4bce059..259bbeb64 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -12,18 +12,48 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
def validate(object, meta)
def validate(%{"type" => "Like"} = object, meta) do
with {:ok, object} <-
- object |> LikeValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do
+ object
+ |> LikeValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object |> Map.from_struct())
{:ok, object, meta}
end
end
+ def validate(%{"type" => "ChatMessage"} = object, meta) do
+ with {:ok, object} <-
+ object
+ |> ChatMessageValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ object = stringify_keys(object)
+ {:ok, object, meta}
+ end
+ end
+
+ def validate(%{"type" => "Create"} = object, meta) do
+ with {:ok, object} <-
+ object
+ |> CreateChatMessageValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ object = stringify_keys(object)
+ {:ok, object, meta}
+ end
+ end
+
+ def stringify_keys(%{__struct__: _} = object) do
+ object
+ |> Map.from_struct()
+ |> stringify_keys
+ end
+
def stringify_keys(object) do
object
|> Map.new(fn {key, val} -> {to_string(key), val} end)
diff --git a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
new file mode 100644
index 000000000..a4e4460cd
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
@@ -0,0 +1,84 @@
+# 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.ObjectValidators.ChatMessageValidator do
+ use Ecto.Schema
+
+ alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+ alias Pleroma.User
+
+ import Ecto.Changeset
+
+ @primary_key false
+ @derive Jason.Encoder
+
+ embedded_schema do
+ field(:id, Types.ObjectID, primary_key: true)
+ field(:to, Types.Recipients, default: [])
+ field(:type, :string)
+ field(:content, :string)
+ field(:actor, Types.ObjectID)
+ field(:published, Types.DateTime)
+ end
+
+ def cast_and_apply(data) do
+ data
+ |> cast_data
+ |> apply_action(:insert)
+ end
+
+ def cast_and_validate(data) do
+ data
+ |> cast_data()
+ |> validate_data()
+ end
+
+ def cast_data(data) do
+ %__MODULE__{}
+ |> changeset(data)
+ end
+
+ def fix(data) do
+ data
+ |> Map.put_new("actor", data["attributedTo"])
+ end
+
+ def changeset(struct, data) do
+ data = fix(data)
+
+ struct
+ |> cast(data, __schema__(:fields))
+ end
+
+ def validate_data(data_cng) do
+ data_cng
+ |> validate_inclusion(:type, ["ChatMessage"])
+ |> validate_required([:id, :actor, :to, :type, :content])
+ |> validate_length(:to, is: 1)
+ |> validate_local_concern()
+ end
+
+ @doc "Validates if at least one of the users in this ChatMessage is a local user, otherwise we don't want the message in our system. It also validates the presence of both users in our system."
+ def validate_local_concern(cng) do
+ with actor_ap <- get_field(cng, :actor),
+ {_, %User{} = actor} <- {:find_actor, User.get_cached_by_ap_id(actor_ap)},
+ {_, %User{} = recipient} <-
+ {:find_recipient, User.get_cached_by_ap_id(get_field(cng, :to) |> hd())},
+ {_, true} <- {:local?, Enum.any?([actor, recipient], & &1.local)} do
+ cng
+ else
+ {:local?, false} ->
+ cng
+ |> add_error(:actor, "actor and recipient are both remote")
+
+ {:find_actor, _} ->
+ cng
+ |> add_error(:actor, "can't find user")
+
+ {:find_recipient, _} ->
+ cng
+ |> add_error(:to, "can't find user")
+ end
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
index b479c3918..02f3a6438 100644
--- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
@@ -8,7 +8,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
alias Pleroma.Object
alias Pleroma.User
- def validate_actor_presence(cng, field_name \\ :actor) do
+ def validate_actor_presence(cng) do
+ validate_user_presence(cng, :actor)
+ end
+
+ def validate_user_presence(cng, field_name) do
cng
|> validate_change(field_name, fn field_name, actor ->
if User.get_cached_by_ap_id(actor) do
diff --git a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex
new file mode 100644
index 000000000..ce52d5623
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex
@@ -0,0 +1,40 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+# NOTES
+# - Can probably be a generic create validator
+# - doesn't embed, will only get the object id
+# - object has to be validated first, maybe with some meta info from the surrounding create
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator do
+ use Ecto.Schema
+
+ alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+
+ import Ecto.Changeset
+
+ @primary_key false
+
+ embedded_schema do
+ field(:id, Types.ObjectID, primary_key: true)
+ field(:actor, Types.ObjectID)
+ field(:type, :string)
+ field(:to, Types.Recipients, default: [])
+ field(:object, Types.ObjectID)
+ end
+
+ def cast_and_apply(data) do
+ data
+ |> cast_data
+ |> apply_action(:insert)
+ end
+
+ def cast_data(data) do
+ cast(%__MODULE__{}, data, __schema__(:fields))
+ end
+
+ # No validation yet
+ def cast_and_validate(data) do
+ cast_data(data)
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/create_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex
index 926804ce7..926804ce7 100644
--- a/lib/pleroma/web/activity_pub/object_validators/create_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex
diff --git a/lib/pleroma/web/activity_pub/object_validators/types/recipients.ex b/lib/pleroma/web/activity_pub/object_validators/types/recipients.ex
new file mode 100644
index 000000000..5a3040842
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/types/recipients.ex
@@ -0,0 +1,23 @@
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.Recipients do
+ use Ecto.Type
+
+ def type, do: {:array, :string}
+
+ def cast(object) when is_binary(object) do
+ cast([object])
+ end
+
+ def cast([_ | _] = data), do: {:ok, data}
+
+ def cast(_) do
+ :error
+ end
+
+ def dump(data) do
+ {:ok, data}
+ end
+
+ def load(data) do
+ {:ok, data}
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index 666a4e310..594f32700 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -5,8 +5,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
liked object, a `Follow` activity will add the user to the follower
collection, and so on.
"""
+ alias Pleroma.Chat
alias Pleroma.Notification
alias Pleroma.Object
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.Utils
def handle(object, meta \\ [])
@@ -21,8 +23,35 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, object, meta}
end
+ def handle(%{data: %{"type" => "Create", "object" => object_id}} = activity, meta) do
+ object = Object.get_by_ap_id(object_id)
+
+ {:ok, _object} = handle_object_creation(object)
+
+ {:ok, activity, meta}
+ end
+
# Nothing to do
def handle(object, meta) do
{:ok, object, meta}
end
+
+ def handle_object_creation(%{data: %{"type" => "ChatMessage"}} = object) do
+ actor = User.get_cached_by_ap_id(object.data["actor"])
+ recipient = User.get_cached_by_ap_id(hd(object.data["to"]))
+
+ [[actor, recipient], [recipient, actor]]
+ |> Enum.each(fn [user, other_user] ->
+ if user.local do
+ Chat.bump_or_create(user.id, other_user.ap_id)
+ end
+ end)
+
+ {:ok, object}
+ end
+
+ # Nothing to do
+ def handle_object_creation(object) do
+ {:ok, object}
+ end
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 39feae285..ad77a5037 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -16,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
alias Pleroma.Web.ActivityPub.Pipeline
+ alias Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageHandling
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator
@@ -643,6 +644,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> handle_incoming(options)
end
+ def handle_incoming(
+ %{"type" => "Create", "object" => %{"type" => "ChatMessage"}} = data,
+ options
+ ),
+ do: ChatMessageHandling.handle_incoming(data, options)
+
def handle_incoming(%{"type" => "Like"} = data, _options) do
with {_, {:ok, cast_data_sym}} <-
{:casting_data,
diff --git a/lib/pleroma/web/activity_pub/transmogrifier/chat_message_handling.ex b/lib/pleroma/web/activity_pub/transmogrifier/chat_message_handling.ex
new file mode 100644
index 000000000..815b866c9
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/transmogrifier/chat_message_handling.ex
@@ -0,0 +1,33 @@
+# 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.Transmogrifier.ChatMessageHandling do
+ alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.ObjectValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
+ alias Pleroma.Web.ActivityPub.Pipeline
+
+ def handle_incoming(
+ %{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object_data} = data,
+ _options
+ ) do
+ with {_, {:ok, cast_data_sym}} <-
+ {:casting_data, data |> CreateChatMessageValidator.cast_and_apply()},
+ cast_data = ObjectValidator.stringify_keys(cast_data_sym),
+ {_, {:ok, object_cast_data_sym}} <-
+ {:casting_object_data, object_data |> ChatMessageValidator.cast_and_apply()},
+ object_cast_data = ObjectValidator.stringify_keys(object_cast_data_sym),
+ {_, {:ok, validated_object, _meta}} <-
+ {:validate_object, ObjectValidator.validate(object_cast_data, %{})},
+ {_, {:ok, _created_object}} <- {:persist_object, Object.create(validated_object)},
+ {_, {:ok, activity, _meta}} <-
+ {:common_pipeline, Pipeline.common_pipeline(cast_data, local: false)} do
+ {:ok, activity}
+ else
+ e ->
+ {:error, e}
+ end
+ end
+end