logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

object_validator.ex (11060B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.ObjectValidator do
  5. @moduledoc """
  6. This module is responsible for validating an object (which can be an activity)
  7. and checking if it is both well formed and also compatible with our view of
  8. the system.
  9. """
  10. @behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
  11. alias Pleroma.Activity
  12. alias Pleroma.EctoType.ActivityPub.ObjectValidators
  13. alias Pleroma.Object
  14. alias Pleroma.Object.Containment
  15. alias Pleroma.User
  16. alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator
  17. alias Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator
  18. alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
  19. alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator
  20. alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
  21. alias Pleroma.Web.ActivityPub.ObjectValidators.AudioImageVideoValidator
  22. alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
  23. alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
  24. alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
  25. alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator
  26. alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
  27. alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
  28. alias Pleroma.Web.ActivityPub.ObjectValidators.EventValidator
  29. alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
  30. alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
  31. alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
  32. alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
  33. alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
  34. @impl true
  35. def validate(object, meta)
  36. def validate(%{"type" => "Block"} = block_activity, meta) do
  37. with {:ok, block_activity} <-
  38. block_activity
  39. |> BlockValidator.cast_and_validate()
  40. |> Ecto.Changeset.apply_action(:insert) do
  41. block_activity = stringify_keys(block_activity)
  42. outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
  43. meta =
  44. if !outgoing_blocks do
  45. Keyword.put(meta, :do_not_federate, true)
  46. else
  47. meta
  48. end
  49. {:ok, block_activity, meta}
  50. end
  51. end
  52. def validate(%{"type" => "Undo"} = object, meta) do
  53. with {:ok, object} <-
  54. object
  55. |> UndoValidator.cast_and_validate()
  56. |> Ecto.Changeset.apply_action(:insert) do
  57. object = stringify_keys(object)
  58. undone_object = Activity.get_by_ap_id(object["object"])
  59. meta =
  60. meta
  61. |> Keyword.put(:object_data, undone_object.data)
  62. {:ok, object, meta}
  63. end
  64. end
  65. def validate(%{"type" => "Delete"} = object, meta) do
  66. with cng <- DeleteValidator.cast_and_validate(object),
  67. do_not_federate <- DeleteValidator.do_not_federate?(cng),
  68. {:ok, object} <- Ecto.Changeset.apply_action(cng, :insert) do
  69. object = stringify_keys(object)
  70. meta = Keyword.put(meta, :do_not_federate, do_not_federate)
  71. {:ok, object, meta}
  72. end
  73. end
  74. def validate(
  75. %{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object} = create_activity,
  76. meta
  77. ) do
  78. with {:ok, object_data} <- cast_and_apply(object),
  79. meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
  80. {:ok, create_activity} <-
  81. create_activity
  82. |> CreateChatMessageValidator.cast_and_validate(meta)
  83. |> Ecto.Changeset.apply_action(:insert) do
  84. create_activity = stringify_keys(create_activity)
  85. {:ok, create_activity, meta}
  86. end
  87. end
  88. def validate(
  89. %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
  90. meta
  91. )
  92. when objtype in ~w[Question Answer Audio Video Image Event Article Note Page] do
  93. with {:ok, object_data} <- cast_and_apply_and_stringify_with_history(object),
  94. meta = Keyword.put(meta, :object_data, object_data),
  95. {:ok, create_activity} <-
  96. create_activity
  97. |> CreateGenericValidator.cast_and_validate(meta)
  98. |> Ecto.Changeset.apply_action(:insert) do
  99. create_activity = stringify_keys(create_activity)
  100. {:ok, create_activity, meta}
  101. end
  102. end
  103. def validate(%{"type" => type} = object, meta)
  104. when type in ~w[Event Question Audio Video Image Article Note Page] do
  105. validator =
  106. case type do
  107. "Event" -> EventValidator
  108. "Question" -> QuestionValidator
  109. "Audio" -> AudioImageVideoValidator
  110. "Video" -> AudioImageVideoValidator
  111. "Image" -> AudioImageVideoValidator
  112. "Article" -> ArticleNotePageValidator
  113. "Note" -> ArticleNotePageValidator
  114. "Page" -> ArticleNotePageValidator
  115. end
  116. with {:ok, object} <-
  117. do_separate_with_history(object, fn object ->
  118. with {:ok, object} <-
  119. object
  120. |> validator.cast_and_validate()
  121. |> Ecto.Changeset.apply_action(:insert) do
  122. object = stringify_keys(object)
  123. # Insert copy of hashtags as strings for the non-hashtag table indexing
  124. tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object})
  125. object = Map.put(object, "tag", tag)
  126. {:ok, object}
  127. end
  128. end) do
  129. {:ok, object, meta}
  130. end
  131. end
  132. def validate(
  133. %{"type" => "Update", "object" => %{"type" => objtype} = object} = update_activity,
  134. meta
  135. )
  136. when objtype in ~w[Question Answer Audio Video Event Article Note Page] do
  137. with {_, false} <- {:local, Access.get(meta, :local, false)},
  138. {_, {:ok, object_data, _}} <- {:object_validation, validate(object, meta)},
  139. meta = Keyword.put(meta, :object_data, object_data),
  140. {:ok, update_activity} <-
  141. update_activity
  142. |> UpdateValidator.cast_and_validate()
  143. |> Ecto.Changeset.apply_action(:insert) do
  144. update_activity = stringify_keys(update_activity)
  145. {:ok, update_activity, meta}
  146. else
  147. {:local, _} ->
  148. with {:ok, object} <-
  149. update_activity
  150. |> UpdateValidator.cast_and_validate()
  151. |> Ecto.Changeset.apply_action(:insert) do
  152. object = stringify_keys(object)
  153. {:ok, object, meta}
  154. end
  155. {:object_validation, e} ->
  156. e
  157. {:error, %Ecto.Changeset{} = e} ->
  158. {:error, e}
  159. end
  160. end
  161. def validate(%{"type" => type} = object, meta)
  162. when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
  163. ChatMessage Answer] do
  164. validator =
  165. case type do
  166. "Accept" -> AcceptRejectValidator
  167. "Reject" -> AcceptRejectValidator
  168. "Follow" -> FollowValidator
  169. "Update" -> UpdateValidator
  170. "Like" -> LikeValidator
  171. "EmojiReact" -> EmojiReactValidator
  172. "Announce" -> AnnounceValidator
  173. "ChatMessage" -> ChatMessageValidator
  174. "Answer" -> AnswerValidator
  175. end
  176. with {:ok, object} <-
  177. object
  178. |> validator.cast_and_validate()
  179. |> Ecto.Changeset.apply_action(:insert) do
  180. object = stringify_keys(object)
  181. {:ok, object, meta}
  182. end
  183. end
  184. def validate(%{"type" => type} = object, meta) when type in ~w(Add Remove) do
  185. with {:ok, object} <-
  186. object
  187. |> AddRemoveValidator.cast_and_validate()
  188. |> Ecto.Changeset.apply_action(:insert) do
  189. object = stringify_keys(object)
  190. {:ok, object, meta}
  191. end
  192. end
  193. def validate(o, m), do: {:error, {:validator_not_set, {o, m}}}
  194. def cast_and_apply_and_stringify_with_history(object) do
  195. do_separate_with_history(object, fn object ->
  196. with {:ok, object_data} <- cast_and_apply(object),
  197. object_data <- object_data |> stringify_keys() do
  198. {:ok, object_data}
  199. end
  200. end)
  201. end
  202. def cast_and_apply(%{"type" => "ChatMessage"} = object) do
  203. ChatMessageValidator.cast_and_apply(object)
  204. end
  205. def cast_and_apply(%{"type" => "Question"} = object) do
  206. QuestionValidator.cast_and_apply(object)
  207. end
  208. def cast_and_apply(%{"type" => "Answer"} = object) do
  209. AnswerValidator.cast_and_apply(object)
  210. end
  211. def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Image Video] do
  212. AudioImageVideoValidator.cast_and_apply(object)
  213. end
  214. def cast_and_apply(%{"type" => "Event"} = object) do
  215. EventValidator.cast_and_apply(object)
  216. end
  217. def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page] do
  218. ArticleNotePageValidator.cast_and_apply(object)
  219. end
  220. def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
  221. def stringify_keys(object) when is_struct(object) do
  222. object
  223. |> Map.from_struct()
  224. |> stringify_keys
  225. end
  226. def stringify_keys(object) when is_map(object) do
  227. object
  228. |> Enum.filter(fn {_, v} -> v != nil end)
  229. |> Map.new(fn {key, val} -> {to_string(key), stringify_keys(val)} end)
  230. end
  231. def stringify_keys(object) when is_list(object) do
  232. object
  233. |> Enum.map(&stringify_keys/1)
  234. end
  235. def stringify_keys(object), do: object
  236. def fetch_actor(object) do
  237. with actor <- Containment.get_actor(object),
  238. {:ok, actor} <- ObjectValidators.ObjectID.cast(actor) do
  239. User.get_or_fetch_by_ap_id(actor)
  240. end
  241. end
  242. def fetch_actor_and_object(object) do
  243. fetch_actor(object)
  244. Object.normalize(object["object"], fetch: true)
  245. :ok
  246. end
  247. defp for_each_history_item(
  248. %{"type" => "OrderedCollection", "orderedItems" => items} = history,
  249. object,
  250. fun
  251. ) do
  252. processed_items =
  253. Enum.map(items, fn item ->
  254. with item <- Map.put(item, "id", object["id"]),
  255. {:ok, item} <- fun.(item) do
  256. item
  257. else
  258. _ -> nil
  259. end
  260. end)
  261. if Enum.all?(processed_items, &(not is_nil(&1))) do
  262. {:ok, Map.put(history, "orderedItems", processed_items)}
  263. else
  264. {:error, :invalid_history}
  265. end
  266. end
  267. defp for_each_history_item(nil, _object, _fun) do
  268. {:ok, nil}
  269. end
  270. defp for_each_history_item(_, _object, _fun) do
  271. {:error, :invalid_history}
  272. end
  273. # fun is (object -> {:ok, validated_object_with_string_keys})
  274. defp do_separate_with_history(object, fun) do
  275. with history <- object["formerRepresentations"],
  276. object <- Map.drop(object, ["formerRepresentations"]),
  277. {_, {:ok, object}} <- {:main_body, fun.(object)},
  278. {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do
  279. object =
  280. if history do
  281. Map.put(object, "formerRepresentations", history)
  282. else
  283. object
  284. end
  285. {:ok, object}
  286. else
  287. {:main_body, e} -> e
  288. {:history_items, e} -> e
  289. end
  290. end
  291. end