ActivityPub: Remove `update` and switch to pipeline.
authorlain <lain@soykaf.club>
Mon, 22 Jun 2020 11:59:45 +0000 (13:59 +0200)
committerlain <lain@soykaf.club>
Mon, 22 Jun 2020 11:59:45 +0000 (13:59 +0200)
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/side_effects.ex
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/side_effects_test.exs

index 3e4f3ad30804290f7967fbab281598bf6e06d6f5..4cc9fe16c0b6debb849736d9e74c0dbc7ddb903e 100644 (file)
@@ -321,28 +321,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  @spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
-  def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
-    local = !(params[:local] == false)
-    activity_id = params[:activity_id]
-
-    data =
-      %{
-        "to" => to,
-        "cc" => cc,
-        "type" => "Update",
-        "actor" => actor,
-        "object" => object
-      }
-      |> Maps.put_if_present("id", activity_id)
-
-    with {:ok, activity} <- insert(data, local),
-         _ <- notify_and_stream(activity),
-         :ok <- maybe_federate(activity) do
-      {:ok, activity}
-    end
-  end
-
   @spec follow(User.t(), User.t(), String.t() | nil, boolean(), keyword()) ::
           {:ok, Activity.t()} | {:error, any()}
   def follow(follower, followed, activity_id \\ nil, local \\ true, opts \\ []) do
index 09fd7d7c9d7450bcec2004f5d008561ab4c7bee8..de143b8f0a6a90b53cb44d815b4989da9ad79834 100644 (file)
@@ -21,13 +21,21 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
   def handle(object, meta \\ [])
 
   # Tasks this handles:
-  # Update the user
+  # - Update the user
+  #
+  # For a local user, we also get a changeset with the full information, so we
+  # can update non-federating, non-activitypub settings as well.
   def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do
-    {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object)
+    if changeset = Keyword.get(meta, :user_update_changeset) do
+      changeset
+      |> User.update_and_set_cache()
+    else
+      {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object)
 
-    User.get_by_ap_id(updated_object["id"])
-    |> User.remote_user_changeset(new_user_data)
-    |> User.update_and_set_cache()
+      User.get_by_ap_id(updated_object["id"])
+      |> User.remote_user_changeset(new_user_data)
+      |> User.update_and_set_cache()
+    end
 
     {:ok, object, meta}
   end
index c38c2b895b09a8631886273141757521af4710df..f0499621a59bf3733de0ddf96de5d3d65e8d3fb9 100644 (file)
@@ -20,6 +20,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
   alias Pleroma.Plugs.RateLimiter
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Pipeline
+  alias Pleroma.Web.ActivityPub.Builder
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.MastodonAPI.ListView
   alias Pleroma.Web.MastodonAPI.MastodonAPI
@@ -179,34 +181,39 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
       |> Maps.put_if_present(:default_scope, params["source"]["privacy"])
       |> Maps.put_if_present(:actor_type, params[:actor_type])
 
-    changeset = User.update_changeset(user, user_params)
-
-    with {:ok, user} <- User.update_and_set_cache(changeset) do
-      user
-      |> build_update_activity_params()
-      |> ActivityPub.update()
-
-      render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
+    # What happens here:
+    #
+    # We want to update the user through the pipeline, but the ActivityPub
+    # update information is not quite enough for this, because this also
+    # contains local settings that don't federate and don't even appear
+    # in the Update activity. 
+    # 
+    # So we first build the normal local changeset, then apply it to the
+    # user data, but don't persist it. With this, we generate the object
+    # data for our update activity. We feed this and the changeset as meta
+    # inforation into the pipeline, where they will be properly updated and
+    # federated.
+    with changeset <- User.update_changeset(user, user_params),
+         {:ok, unpersisted_user} <- Ecto.Changeset.apply_action(changeset, :update),
+         updated_object <-
+           Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
+           |> Map.delete("@context"),
+         {:ok, update_data, []} <- Builder.update(user, updated_object),
+         {:ok, _update, _} <-
+           Pipeline.common_pipeline(update_data,
+             local: true,
+             user_update_changeset: changeset
+           ) do
+      render(conn, "show.json",
+        user: unpersisted_user,
+        for: unpersisted_user,
+        with_pleroma_settings: true
+      )
     else
       _e -> render_error(conn, :forbidden, "Invalid request")
     end
   end
 
-  # Hotfix, handling will be redone with the pipeline
-  defp build_update_activity_params(user) do
-    object =
-      Pleroma.Web.ActivityPub.UserView.render("user.json", user: user)
-      |> Map.delete("@context")
-
-    %{
-      local: true,
-      to: [user.follower_address],
-      cc: [],
-      object: object,
-      actor: user.ap_id
-    }
-  end
-
   defp normalize_fields_attributes(fields) do
     if Enum.all?(fields, &is_tuple/1) do
       Enum.map(fields, fn {_, v} -> v end)
index 7693f6400d07524cedd0bed93b5a716365999b26..ce35c96051f7cae6dfead2910f62c408628a9fa3 100644 (file)
@@ -1092,52 +1092,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     end
   end
 
-  describe "update" do
-    setup do: clear_config([:instance, :max_pinned_statuses])
-
-    test "it creates an update activity with the new user data" do
-      user = insert(:user)
-      {:ok, user} = User.ensure_keys_present(user)
-      user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
-
-      {:ok, update} =
-        ActivityPub.update(%{
-          actor: user_data["id"],
-          to: [user.follower_address],
-          cc: [],
-          object: user_data
-        })
-
-      assert update.data["actor"] == user.ap_id
-      assert update.data["to"] == [user.follower_address]
-      assert embedded_object = update.data["object"]
-      assert embedded_object["id"] == user_data["id"]
-      assert embedded_object["type"] == user_data["type"]
-    end
-  end
-
-  test "returned pinned statuses" do
-    Config.put([:instance, :max_pinned_statuses], 3)
-    user = insert(:user)
-
-    {:ok, activity_one} = CommonAPI.post(user, %{status: "HI!!!"})
-    {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
-    {:ok, activity_three} = CommonAPI.post(user, %{status: "HI!!!"})
-
-    CommonAPI.pin(activity_one.id, user)
-    user = refresh_record(user)
-
-    CommonAPI.pin(activity_two.id, user)
-    user = refresh_record(user)
-
-    CommonAPI.pin(activity_three.id, user)
-    user = refresh_record(user)
-
-    activities = ActivityPub.fetch_user_activities(user, nil, %{pinned: true})
-
-    assert 3 = length(activities)
-  end
-
   describe "flag/1" do
     setup do
       reporter = insert(:user)
index 1d7c2736b6ecc0c81a0adbf7d2ccd42516fa6901..12c9ef1da6e2dd3efc893b81d922145eb18c68f5 100644 (file)
@@ -78,6 +78,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
       user = User.get_by_id(user.id)
       assert user.name == "new name!"
     end
+
+    test "it uses a given changeset to update", %{user: user, update: update} do
+      changeset = Ecto.Changeset.change(user, %{default_scope: "direct"})
+
+      assert user.default_scope == "public"
+      {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset)
+      user = User.get_by_id(user.id)
+      assert user.default_scope == "direct"
+    end
   end
 
   describe "delete objects" do