logo

pleroma

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

object_validator.ex (12081B)


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