pleroma/lib/pleroma/web/activity_pub/object_validator.ex

293 lines
9.2 KiB
Elixir
Raw Normal View History

# Pleroma: A lightweight social networking server
2020-03-26 15:37:42 +01:00
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidator do
@moduledoc """
This module is responsible for validating an object (which can be an activity)
and checking if it is both well formed and also compatible with our view of
the system.
"""
alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators
2020-06-17 09:31:06 +02:00
alias Pleroma.Object
2020-08-20 02:00:04 +02:00
alias Pleroma.Object.Containment
2019-10-23 12:18:05 +02:00
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
2020-06-11 20:43:01 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator
2020-08-20 20:03:07 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator
2020-08-20 02:00:04 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
2020-04-08 15:55:43 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
2020-08-20 18:41:42 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.EventValidator
2020-07-06 15:57:19 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
2020-04-20 12:29:19 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
2020-06-11 20:23:10 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
2020-05-07 15:09:37 +02:00
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
def validate(object, meta)
def validate(%{"type" => type} = object, meta)
when type in ~w[Accept Reject] do
with {:ok, object} <-
object
|> AcceptRejectValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-08-20 18:41:42 +02:00
def validate(%{"type" => "Event"} = object, meta) do
with {:ok, object} <-
object
|> EventValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-07-06 15:57:19 +02:00
def validate(%{"type" => "Follow"} = object, meta) do
with {:ok, object} <-
object
|> FollowValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
def validate(%{"type" => "Block"} = block_activity, meta) do
with {:ok, block_activity} <-
block_activity
|> BlockValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
block_activity = stringify_keys(block_activity)
outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
meta =
if !outgoing_blocks do
Keyword.put(meta, :do_not_federate, true)
else
meta
end
{:ok, block_activity, meta}
end
end
def validate(%{"type" => "Update"} = update_activity, meta) do
with {:ok, update_activity} <-
update_activity
|> UpdateValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
update_activity = stringify_keys(update_activity)
{:ok, update_activity, meta}
end
end
2020-05-05 14:17:47 +02:00
def validate(%{"type" => "Undo"} = object, meta) do
with {:ok, object} <-
2020-05-07 14:52:37 +02:00
object
|> UndoValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
undone_object = Activity.get_by_ap_id(object["object"])
meta =
meta
|> Keyword.put(:object_data, undone_object.data)
2020-05-05 14:17:47 +02:00
{:ok, object, meta}
end
end
def validate(%{"type" => "Delete"} = object, meta) do
with cng <- DeleteValidator.cast_and_validate(object),
do_not_federate <- DeleteValidator.do_not_federate?(cng),
{:ok, object} <- Ecto.Changeset.apply_action(cng, :insert) do
object = stringify_keys(object)
meta = Keyword.put(meta, :do_not_federate, do_not_federate)
{:ok, object, meta}
end
end
def validate(%{"type" => "Like"} = object, meta) do
with {:ok, object} <-
2020-04-08 15:55:43 +02:00
object
|> LikeValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-04-08 15:55:43 +02:00
def validate(%{"type" => "ChatMessage"} = object, meta) do
with {:ok, object} <-
object
2020-04-16 15:21:47 +02:00
|> ChatMessageValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
2020-04-08 15:55:43 +02:00
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-06-11 20:23:10 +02:00
def validate(%{"type" => "Question"} = object, meta) do
with {:ok, object} <-
object
|> QuestionValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-08-20 02:00:04 +02:00
def validate(%{"type" => type} = object, meta) when type in ~w[Audio Video] do
2020-08-18 01:48:42 +02:00
with {:ok, object} <-
object
2020-08-20 02:00:04 +02:00
|> AudioVideoValidator.cast_and_validate()
2020-08-18 01:48:42 +02:00
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-08-20 20:03:07 +02:00
def validate(%{"type" => "Article"} = object, meta) do
with {:ok, object} <-
object
|> ArticleNoteValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-06-11 20:43:01 +02:00
def validate(%{"type" => "Answer"} = object, meta) do
with {:ok, object} <-
object
|> AnswerValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object)
{:ok, object, meta}
end
end
def validate(%{"type" => "EmojiReact"} = object, meta) do
with {:ok, object} <-
object
|> EmojiReactValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
2020-06-11 20:23:10 +02:00
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-06-11 20:23:10 +02:00
def validate(
%{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object} = create_activity,
meta
) do
2020-04-28 17:29:54 +02:00
with {:ok, object_data} <- cast_and_apply(object),
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
{:ok, create_activity} <-
create_activity
|> CreateChatMessageValidator.cast_and_validate(meta)
2020-04-16 15:21:47 +02:00
|> Ecto.Changeset.apply_action(:insert) do
2020-04-28 17:29:54 +02:00
create_activity = stringify_keys(create_activity)
{:ok, create_activity, meta}
2020-04-08 15:55:43 +02:00
end
end
2020-06-11 20:23:10 +02:00
def validate(
2020-06-11 20:43:01 +02:00
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
2020-06-11 20:23:10 +02:00
meta
2020-06-11 20:43:01 +02:00
)
2020-08-20 20:03:07 +02:00
when objtype in ~w[Question Answer Audio Video Event Article] do
2020-06-11 20:23:10 +02:00
with {:ok, object_data} <- cast_and_apply(object),
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
{:ok, create_activity} <-
create_activity
|> CreateGenericValidator.cast_and_validate(meta)
2020-06-11 20:23:10 +02:00
|> Ecto.Changeset.apply_action(:insert) do
create_activity = stringify_keys(create_activity)
{:ok, create_activity, meta}
end
end
def validate(%{"type" => "Announce"} = object, meta) do
with {:ok, object} <-
object
|> AnnounceValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert) do
2020-06-11 20:23:10 +02:00
object = stringify_keys(object)
{:ok, object, meta}
end
end
2020-04-28 17:29:54 +02:00
def cast_and_apply(%{"type" => "ChatMessage"} = object) do
ChatMessageValidator.cast_and_apply(object)
end
2020-06-11 20:23:10 +02:00
def cast_and_apply(%{"type" => "Question"} = object) do
QuestionValidator.cast_and_apply(object)
end
2020-06-11 20:43:01 +02:00
def cast_and_apply(%{"type" => "Answer"} = object) do
AnswerValidator.cast_and_apply(object)
2020-06-11 20:43:01 +02:00
end
2020-08-20 02:00:04 +02:00
def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Video] do
AudioVideoValidator.cast_and_apply(object)
2020-08-18 01:48:42 +02:00
end
2020-08-20 18:41:42 +02:00
def cast_and_apply(%{"type" => "Event"} = object) do
EventValidator.cast_and_apply(object)
end
2020-08-20 20:03:07 +02:00
def cast_and_apply(%{"type" => "Article"} = object) do
ArticleNoteValidator.cast_and_apply(object)
end
2020-04-28 17:29:54 +02:00
def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
2020-06-11 20:23:10 +02:00
# is_struct/1 isn't present in Elixir 1.8.x
2020-04-08 15:55:43 +02:00
def stringify_keys(%{__struct__: _} = object) do
object
|> Map.from_struct()
|> stringify_keys
end
2020-05-06 16:12:36 +02:00
def stringify_keys(object) when is_map(object) do
object
2020-05-06 16:12:36 +02:00
|> Map.new(fn {key, val} -> {to_string(key), stringify_keys(val)} end)
end
2020-05-06 16:12:36 +02:00
def stringify_keys(object) when is_list(object) do
object
|> Enum.map(&stringify_keys/1)
end
def stringify_keys(object), do: object
def fetch_actor(object) do
2020-08-20 02:00:04 +02:00
with actor <- Containment.get_actor(object),
{:ok, actor} <- ObjectValidators.ObjectID.cast(actor) do
User.get_or_fetch_by_ap_id(actor)
end
end
def fetch_actor_and_object(object) do
fetch_actor(object)
Object.normalize(object["object"], true)
:ok
end
end