logo

pleroma

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

common_validations.ex (4260B)


  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.ObjectValidators.CommonValidations do
  5. import Ecto.Changeset
  6. alias Pleroma.Activity
  7. alias Pleroma.Object
  8. alias Pleroma.User
  9. @spec validate_any_presence(Ecto.Changeset.t(), [atom()]) :: Ecto.Changeset.t()
  10. def validate_any_presence(cng, fields) do
  11. non_empty =
  12. fields
  13. |> Enum.map(fn field -> get_field(cng, field) end)
  14. |> Enum.any?(fn
  15. nil -> false
  16. [] -> false
  17. _ -> true
  18. end)
  19. if non_empty do
  20. cng
  21. else
  22. fields
  23. |> Enum.reduce(cng, fn field, cng ->
  24. cng
  25. |> add_error(field, "none of #{inspect(fields)} present")
  26. end)
  27. end
  28. end
  29. @spec validate_actor_presence(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
  30. def validate_actor_presence(cng, options \\ []) do
  31. field_name = Keyword.get(options, :field_name, :actor)
  32. cng
  33. |> validate_change(field_name, fn field_name, actor ->
  34. case User.get_cached_by_ap_id(actor) do
  35. %User{is_active: false} ->
  36. [{field_name, "user is deactivated"}]
  37. %User{} ->
  38. []
  39. _ ->
  40. [{field_name, "can't find user"}]
  41. end
  42. end)
  43. end
  44. @spec validate_object_presence(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
  45. def validate_object_presence(cng, options \\ []) do
  46. field_name = Keyword.get(options, :field_name, :object)
  47. allowed_types = Keyword.get(options, :allowed_types, false)
  48. cng
  49. |> validate_change(field_name, fn field_name, object_id ->
  50. object = Object.get_cached_by_ap_id(object_id) || Activity.get_by_ap_id(object_id)
  51. cond do
  52. !object ->
  53. [{field_name, "can't find object"}]
  54. object && allowed_types && object.data["type"] not in allowed_types ->
  55. [{field_name, "object not in allowed types"}]
  56. true ->
  57. []
  58. end
  59. end)
  60. end
  61. @spec validate_object_or_user_presence(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
  62. def validate_object_or_user_presence(cng, options \\ []) do
  63. field_name = Keyword.get(options, :field_name, :object)
  64. options = Keyword.put(options, :field_name, field_name)
  65. actor_cng =
  66. cng
  67. |> validate_actor_presence(options)
  68. object_cng =
  69. cng
  70. |> validate_object_presence(options)
  71. if actor_cng.valid?, do: actor_cng, else: object_cng
  72. end
  73. @spec validate_host_match(Ecto.Changeset.t(), [atom()]) :: Ecto.Changeset.t()
  74. def validate_host_match(cng, fields \\ [:id, :actor]) do
  75. if same_domain?(cng, fields) do
  76. cng
  77. else
  78. fields
  79. |> Enum.reduce(cng, fn field, cng ->
  80. cng
  81. |> add_error(field, "hosts of #{inspect(fields)} aren't matching")
  82. end)
  83. end
  84. end
  85. @spec validate_fields_match(Ecto.Changeset.t(), [atom()]) :: Ecto.Changeset.t()
  86. def validate_fields_match(cng, fields) do
  87. if map_unique?(cng, fields) do
  88. cng
  89. else
  90. fields
  91. |> Enum.reduce(cng, fn field, cng ->
  92. cng
  93. |> add_error(field, "Fields #{inspect(fields)} aren't matching")
  94. end)
  95. end
  96. end
  97. defp map_unique?(cng, fields, func \\ & &1) do
  98. Enum.reduce_while(fields, nil, fn field, acc ->
  99. value =
  100. cng
  101. |> get_field(field)
  102. |> func.()
  103. case {value, acc} do
  104. {value, nil} -> {:cont, value}
  105. {value, value} -> {:cont, value}
  106. _ -> {:halt, false}
  107. end
  108. end)
  109. end
  110. @spec same_domain?(Ecto.Changeset.t(), [atom()]) :: boolean()
  111. def same_domain?(cng, fields \\ [:actor, :object]) do
  112. map_unique?(cng, fields, fn value -> URI.parse(value).host end)
  113. end
  114. # This figures out if a user is able to create, delete or modify something
  115. # based on the domain and superuser status
  116. @spec validate_modification_rights(Ecto.Changeset.t(), atom()) :: Ecto.Changeset.t()
  117. def validate_modification_rights(cng, privilege) do
  118. actor = User.get_cached_by_ap_id(get_field(cng, :actor))
  119. if User.privileged?(actor, privilege) || same_domain?(cng) do
  120. cng
  121. else
  122. cng
  123. |> add_error(:actor, "is not allowed to modify object")
  124. end
  125. end
  126. end