[#210] [TwitterAPI] Made actor be stored for uploads. Added ownership check
authorIvan Tashkinov <ivantashkinov@gmail.com>
Wed, 5 Dec 2018 10:37:06 +0000 (13:37 +0300)
committerIvan Tashkinov <ivantashkinov@gmail.com>
Wed, 5 Dec 2018 10:37:06 +0000 (13:37 +0300)
to `update_media` action. Added controller tests for `upload` and `update_media` actions.
Refactoring.

lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/twitter_api/twitter_api.ex
lib/pleroma/web/twitter_api/twitter_api_controller.ex
test/support/data_case.ex
test/upload_test.exs
test/web/mastodon_api/mastodon_api_controller_test.exs
test/web/twitter_api/twitter_api_controller_test.exs
test/web/twitter_api/twitter_api_test.exs

index 7e207c6201b1eb330c9c4737cfb437dd5a051ef4..39692163f168363bab1ec6f3c7263e0de8ff53a5 100644 (file)
@@ -574,7 +574,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   def upload(file, opts \\ []) do
     with {:ok, data} <- Upload.store(file, opts) do
-      Repo.insert(%Object{data: data})
+      obj_data = if opts[:actor], do: Map.put(data, "actor", opts[:actor]), else: data
+      Repo.insert(%Object{data: obj_data})
     end
   end
 
index c19a4f0849d6ce85862cdfce1f1d5cc48a886058..b9468ab0369aae55a3e4503c65615244dceefccf 100644 (file)
@@ -93,8 +93,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
     end
   end
 
-  def upload(%Plug.Upload{} = file, format \\ "xml") do
-    {:ok, object} = ActivityPub.upload(file)
+  def ap_upload(%Plug.Upload{} = file, %User{} = user) do
+    ActivityPub.upload(file, actor: User.ap_id(user))
+  end
+
+  def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do
+    {:ok, object} = ap_upload(file, user)
 
     url = List.first(object.data["url"])
     href = url["href"]
index c9e845aea2f8667bccc6fbd9e5ecb494f522a510..2f12131e7cb20949966845199e165c33e76a6e2b 100644 (file)
@@ -230,34 +230,47 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
   Updates metadata of uploaded media object.
   Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create).
   """
-  def update_media(%{assigns: %{user: _}} = conn, %{"media_id" => id} = data) do
+  def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do
+    object = Repo.get(Object, id)
     description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"]
 
-    with %Object{} = object <- Repo.get(Object, id),
-         is_binary(description) do
-      new_data = Map.put(object.data, "name", description)
+    {conn, status, response_body} =
+      cond do
+        !object ->
+          {halt(conn), :not_found, ""}
 
-      {:ok, _} =
-        object
-        |> Object.change(%{data: new_data})
-        |> Repo.update()
-    end
+        object.data["actor"] != User.ap_id(user) ->
+          {halt(conn), :forbidden, "You can only update your own uploads."}
+
+        !is_binary(description) ->
+          {conn, :not_modified, ""}
+
+        true ->
+          new_data = Map.put(object.data, "name", description)
+
+          {:ok, _} =
+            object
+            |> Object.change(%{data: new_data})
+            |> Repo.update()
+
+          {conn, :no_content, ""}
+      end
 
     conn
-    |> put_status(:no_content)
-    |> json("")
+    |> put_status(status)
+    |> json(response_body)
   end
 
-  def upload(conn, %{"media" => media}) do
-    response = TwitterAPI.upload(media)
+  def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do
+    response = TwitterAPI.upload(media, user)
 
     conn
     |> put_resp_content_type("application/atom+xml")
     |> send_resp(200, response)
   end
 
-  def upload_json(conn, %{"media" => media}) do
-    response = TwitterAPI.upload(media, "json")
+  def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do
+    response = TwitterAPI.upload(media, user, "json")
 
     conn
     |> json_reply(200, response)
index 8eff0fd946185470784959f3aa7a9b03695d1db4..9dde6b5e552160b7e9f137b283df845724900e72 100644 (file)
@@ -36,6 +36,23 @@ defmodule Pleroma.DataCase do
     :ok
   end
 
+  def ensure_local_uploader(_context) do
+    uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
+    filters = Pleroma.Config.get([Pleroma.Upload, :filters])
+
+    unless uploader == Pleroma.Uploaders.Local || filters != [] do
+      Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
+      Pleroma.Config.put([Pleroma.Upload, :filters], [])
+
+      on_exit(fn ->
+        Pleroma.Config.put([Pleroma.Upload, :uploader], uploader)
+        Pleroma.Config.put([Pleroma.Upload, :filters], filters)
+      end)
+    end
+
+    :ok
+  end
+
   @doc """
   A helper that transform changeset errors to a map of messages.
 
index b2ce755d2b44fa42ec2f6cf33ab1aa55f410966f..f2cad4cf0cccaee9489ce88fdd5323610dc7198a 100644 (file)
@@ -3,22 +3,7 @@ defmodule Pleroma.UploadTest do
   use Pleroma.DataCase
 
   describe "Storing a file with the Local uploader" do
-    setup do
-      uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
-      filters = Pleroma.Config.get([Pleroma.Upload, :filters])
-
-      unless uploader == Pleroma.Uploaders.Local || filters != [] do
-        Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
-        Pleroma.Config.put([Pleroma.Upload, :filters], [])
-
-        on_exit(fn ->
-          Pleroma.Config.put([Pleroma.Upload, :uploader], uploader)
-          Pleroma.Config.put([Pleroma.Upload, :filters], filters)
-        end)
-      end
-
-      :ok
-    end
+    setup [:ensure_local_uploader]
 
     test "returns a media url" do
       File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
index 098acb59ffa9a65dd3e8a0636929671cffe9ad65..b5839cff10059d1264ebd1d873ebbfd1e41e8315 100644 (file)
@@ -804,7 +804,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       }
 
       media =
-        TwitterAPI.upload(file, "json")
+        TwitterAPI.upload(file, user, "json")
         |> Poison.decode!()
 
       {:ok, image_post} =
index 478763de78db9c5eed35c8de2a318ddc555be684..c07dc69121eb805bfaa8437be71d3bf698f35053 100644 (file)
@@ -1254,15 +1254,74 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
     end
   end
 
-  describe "POST /api/media/metadata/create" do
-    test "it updates `data[name]` of referenced Object with provided value", %{conn: conn} do
+  describe "POST /api/media/upload" do
+    setup context do
+      Pleroma.DataCase.ensure_local_uploader(context)
+    end
+
+    test "it performs the upload and sets `data[actor]` with AP id of uploader user", %{
+      conn: conn
+    } do
       user = insert(:user)
+
+      upload_filename = "test/fixtures/image_tmp.jpg"
+      File.cp!("test/fixtures/image.jpg", upload_filename)
+
+      file = %Plug.Upload{
+        content_type: "image/jpg",
+        path: Path.absname(upload_filename),
+        filename: "image.jpg"
+      }
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> put_req_header("content-type", "application/octet-stream")
+        |> post("/api/media/upload", %{
+          "media" => file
+        })
+        |> json_response(:ok)
+
+      assert response["media_id"]
+      object = Repo.get(Object, response["media_id"])
+      assert object
+      assert object.data["actor"] == User.ap_id(user)
+    end
+  end
+
+  describe "POST /api/media/metadata/create" do
+    setup do
       object = insert(:note)
-      description = "Informative description of the image. Initial: #{object.data["name"]}}"
+      user = User.get_by_ap_id(object.data["actor"])
+      %{object: object, user: user}
+    end
+
+    test "it returns :forbidden status on attempt to modify someone else's upload", %{
+      conn: conn,
+      object: object
+    } do
+      initial_description = object.data["name"]
+      another_user = insert(:user)
+
+      conn
+      |> assign(:user, another_user)
+      |> post("/api/media/metadata/create", %{"media_id" => object.id})
+      |> json_response(:forbidden)
+
+      object = Repo.get(Object, object.id)
+      assert object.data["name"] == initial_description
+    end
+
+    test "it updates `data[name]` of referenced Object with provided value", %{
+      conn: conn,
+      object: object,
+      user: user
+    } do
+      description = "Informative description of the image. Initial value: #{object.data["name"]}}"
 
       conn
       |> assign(:user, user)
-      |> post("/api/media/metadata/create.json", %{
+      |> post("/api/media/metadata/create", %{
         "media_id" => object.id,
         "alt_text" => %{"text" => description}
       })
index 28230699f3e29f33d3939436e4d187f71430011c..e34fbbabd55fceed1d2d61e3cb07a0bdc6588d5e 100644 (file)
@@ -182,13 +182,14 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
   end
 
   test "upload a file" do
+    user = insert(:user)
     file = %Plug.Upload{
       content_type: "image/jpg",
       path: Path.absname("test/fixtures/image.jpg"),
       filename: "an_image.jpg"
     }
 
-    response = TwitterAPI.upload(file)
+    response = TwitterAPI.upload(file, user)
 
     assert is_binary(response)
   end