847d0be6222ea9050fe0f596ad01087b9f03312f
[akkoma] / lib / pleroma / web / activity_pub / object_validators / accept_reject_validator.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.AcceptRejectValidator do
6 use Ecto.Schema
7
8 alias Pleroma.Activity
9 alias Pleroma.Object
10 alias Pleroma.User
11
12 import Ecto.Changeset
13 import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
14
15 @primary_key false
16
17 embedded_schema do
18 quote do
19 unquote do
20 import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
21 message_fields()
22 activity_fields()
23 end
24 end
25 end
26
27 def cast_data(data) do
28 %__MODULE__{}
29 |> cast(data, __schema__(:fields))
30 end
31
32 defp validate_data(cng) do
33 cng
34 |> validate_required([:type, :actor, :to, :cc, :object])
35 |> validate_inclusion(:type, ["Accept", "Reject"])
36 |> validate_actor_presence()
37 |> validate_object_presence(allowed_types: ["Follow"])
38 |> validate_accept_reject_rights()
39 end
40
41 def cast_and_validate(data) do
42 data
43 |> maybe_fetch_object()
44 |> cast_data
45 |> validate_data
46 end
47
48 def validate_accept_reject_rights(cng) do
49 with object_id when is_binary(object_id) <- get_field(cng, :object),
50 %Activity{data: %{"object" => followed_actor}} <- Activity.get_by_ap_id(object_id),
51 true <- followed_actor == get_field(cng, :actor) do
52 cng
53 else
54 _e ->
55 cng
56 |> add_error(:actor, "can't accept or reject the given activity")
57 end
58 end
59
60 defp maybe_fetch_object(%{"object" => %{} = object} = activity) do
61 # If we don't have an ID, we may have to fetch the object
62 if Map.has_key?(object, "id") do
63 # Do nothing
64 activity
65 else
66 Map.put(activity, "object", fetch_transient_object(object))
67 end
68 end
69
70 defp maybe_fetch_object(activity), do: activity
71
72 defp fetch_transient_object(
73 %{"actor" => actor, "object" => target, "type" => "Follow"} = object
74 ) do
75 with %User{} = actor <- User.get_cached_by_ap_id(actor),
76 %User{local: true} = target <- User.get_cached_by_ap_id(target),
77 %Activity{} = activity <- Activity.follow_activity(actor, target) do
78 activity.data
79 else
80 _e ->
81 object
82 end
83 end
84
85 defp fetch_transient_object(_), do: {:error, "not a supported transient object"}
86 end