summaryrefslogtreecommitdiff
path: root/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
blob: 093549a45873124904298545313f2d223812109b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
  import Ecto.Changeset

  alias Pleroma.Activity
  alias Pleroma.Object
  alias Pleroma.User

  def validate_any_presence(cng, fields) do
    non_empty =
      fields
      |> Enum.map(fn field -> get_field(cng, field) end)
      |> Enum.any?(fn
        [] -> false
        _ -> true
      end)

    if non_empty do
      cng
    else
      fields
      |> Enum.reduce(cng, fn field, cng ->
        cng
        |> add_error(field, "none of #{inspect(fields)} present")
      end)
    end
  end

  def validate_actor_presence(cng, options \\ []) do
    field_name = Keyword.get(options, :field_name, :actor)

    cng
    |> validate_change(field_name, fn field_name, actor ->
      case User.get_cached_by_ap_id(actor) do
        %User{is_active: false} ->
          [{field_name, "user is deactivated"}]

        %User{} ->
          []

        _ ->
          [{field_name, "can't find user"}]
      end
    end)
  end

  def validate_object_presence(cng, options \\ []) do
    field_name = Keyword.get(options, :field_name, :object)
    allowed_types = Keyword.get(options, :allowed_types, false)

    cng
    |> validate_change(field_name, fn field_name, object_id ->
      object = Object.get_cached_by_ap_id(object_id) || Activity.get_by_ap_id(object_id)

      cond do
        !object ->
          [{field_name, "can't find object"}]

        object && allowed_types && object.data["type"] not in allowed_types ->
          [{field_name, "object not in allowed types"}]

        true ->
          []
      end
    end)
  end

  def validate_object_or_user_presence(cng, options \\ []) do
    field_name = Keyword.get(options, :field_name, :object)
    options = Keyword.put(options, :field_name, field_name)

    actor_cng =
      cng
      |> validate_actor_presence(options)

    object_cng =
      cng
      |> validate_object_presence(options)

    if actor_cng.valid?, do: actor_cng, else: object_cng
  end

  def validate_host_match(cng, fields \\ [:id, :actor]) do
    if same_domain?(cng, fields) do
      cng
    else
      fields
      |> Enum.reduce(cng, fn field, cng ->
        cng
        |> add_error(field, "hosts of #{inspect(fields)} aren't matching")
      end)
    end
  end

  def validate_fields_match(cng, fields) do
    if map_unique?(cng, fields) do
      cng
    else
      fields
      |> Enum.reduce(cng, fn field, cng ->
        cng
        |> add_error(field, "Fields #{inspect(fields)} aren't matching")
      end)
    end
  end

  defp map_unique?(cng, fields, func \\ & &1) do
    Enum.reduce_while(fields, nil, fn field, acc ->
      value =
        cng
        |> get_field(field)
        |> func.()

      case {value, acc} do
        {value, nil} -> {:cont, value}
        {value, value} -> {:cont, value}
        _ -> {:halt, false}
      end
    end)
  end

  def same_domain?(cng, fields \\ [:actor, :object]) do
    map_unique?(cng, fields, fn value -> URI.parse(value).host end)
  end

  # This figures out if a user is able to create, delete or modify something
  # based on the domain and superuser status
  def validate_modification_rights(cng) do
    actor = User.get_cached_by_ap_id(get_field(cng, :actor))

    if User.superuser?(actor) || same_domain?(cng) do
      cng
    else
      cng
      |> add_error(:actor, "is not allowed to modify object")
    end
  end
end