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