Fix addressing
[akkoma] / lib / pleroma / web / activity_pub / object_validators / create_generic_validator.ex
index f467ccc7cfcece6710be8b390e43cef80052e3db..d2de53049d760edf2820b17e6c309c92a83e7b8f 100644 (file)
@@ -1,5 +1,5 @@
 # Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
 # Code based on CreateChatMessageValidator
@@ -8,25 +8,35 @@
 defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
   use Ecto.Schema
 
+  alias Pleroma.EctoType.ActivityPub.ObjectValidators
   alias Pleroma.Object
-  alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
+  alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+  alias Pleroma.Web.ActivityPub.Transmogrifier
 
   import Ecto.Changeset
-  import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
 
   @primary_key false
 
   embedded_schema do
-    field(:id, Types.ObjectID, primary_key: true)
-    field(:actor, Types.ObjectID)
+    field(:id, ObjectValidators.ObjectID, primary_key: true)
+    field(:actor, ObjectValidators.ObjectID)
     field(:type, :string)
-    field(:to, Types.Recipients, default: [])
-    field(:cc, Types.Recipients, default: [])
-    field(:object, Types.ObjectID)
-    field(:expires_at, Types.DateTime)
+    field(:to, ObjectValidators.Recipients, default: [])
+    field(:cc, ObjectValidators.Recipients, default: [])
+    field(:bto, ObjectValidators.Recipients, default: [])
+    field(:bcc, ObjectValidators.Recipients, default: [])
+    field(:object, ObjectValidators.ObjectID)
+    field(:expires_at, ObjectValidators.DateTime)
+
+    # Should be moved to object, done for CommonAPI.Utils.make_context
+    field(:context, :string)
   end
 
-  def cast_data(data) do
+  def cast_data(data, meta \\ []) do
+    data = fix(data, meta)
+
     %__MODULE__{}
     |> changeset(data)
   end
@@ -39,7 +49,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
 
   def cast_and_validate(data, meta \\ []) do
     data
-    |> cast_data
+    |> cast_data(meta)
     |> validate_data(meta)
   end
 
@@ -48,13 +58,37 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
     |> cast(data, __schema__(:fields))
   end
 
-  def validate_data(cng, meta \\ []) do
+  # CommonFixes.fix_activity_addressing adapted for Create specific behavior
+  defp fix_addressing(data, object) do
+    %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["actor"])
+
+    data
+    |> CommonFixes.cast_and_filter_recipients("to", follower_collection, object["to"])
+    |> CommonFixes.cast_and_filter_recipients("cc", follower_collection, object["cc"])
+    |> CommonFixes.cast_and_filter_recipients("bto", follower_collection, object["bto"])
+    |> CommonFixes.cast_and_filter_recipients("bcc", follower_collection, object["bcc"])
+    |> Transmogrifier.fix_implicit_addressing(follower_collection)
+  end
+
+  def fix(data, meta) do
+    object = meta[:object_data]
+
+    data
+    |> CommonFixes.fix_actor()
+    |> Map.put_new("context", object["context"])
+    |> fix_addressing(object)
+  end
+
+  defp validate_data(cng, meta) do
+    object = meta[:object_data]
+
     cng
-    |> validate_required([:actor, :type, :object])
+    |> validate_required([:actor, :type, :object, :to, :cc])
     |> validate_inclusion(:type, ["Create"])
-    |> validate_actor_is_active()
-    |> validate_any_presence([:to, :cc])
-    |> validate_actors_match(meta)
+    |> CommonValidations.validate_actor_presence()
+    |> validate_actors_match(object)
+    |> validate_context_match(object)
+    |> validate_addressing_match(object)
     |> validate_object_nonexistence()
     |> validate_object_containment()
   end
@@ -86,16 +120,42 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
     end)
   end
 
-  def validate_actors_match(cng, meta) do
-    object_actor = meta[:object_data]["actor"]
+  def validate_actors_match(cng, object) do
+    attributed_to = object["attributedTo"] || object["actor"]
 
     cng
     |> validate_change(:actor, fn :actor, actor ->
-      if actor == object_actor do
+      if actor == attributed_to do
         []
       else
-        [{:actor, "Actor doesn't match with object actor"}]
+        [{:actor, "Actor doesn't match with object attributedTo"}]
       end
     end)
   end
+
+  def validate_context_match(cng, %{"context" => object_context}) do
+    cng
+    |> validate_change(:context, fn :context, context ->
+      if context == object_context do
+        []
+      else
+        [{:context, "context field not matching between Create and object (#{object_context})"}]
+      end
+    end)
+  end
+
+  def validate_addressing_match(cng, object) do
+    [:to, :cc, :bcc, :bto]
+    |> Enum.reduce(cng, fn field, cng ->
+      object_data = object[to_string(field)]
+
+      validate_change(cng, field, fn field, data ->
+        if data == object_data do
+          []
+        else
+          [{field, "field doesn't match with object (#{inspect(object_data)})"}]
+        end
+      end)
+    end)
+  end
 end