From c077dc7af5e2a378223a8d2862df1d52877ea245 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Oct 2019 11:52:21 -0500 Subject: [PATCH 01/14] Initial doc about storing remote media --- docs/administration/storing_remote_media.md | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/administration/storing_remote_media.md diff --git a/docs/administration/storing_remote_media.md b/docs/administration/storing_remote_media.md new file mode 100644 index 000000000..7edda2753 --- /dev/null +++ b/docs/administration/storing_remote_media.md @@ -0,0 +1,36 @@ +# Storing Remote Media + +Pleroma does not store remote/federated media by default. The best way to achieve this is to change Nginx to keep its reverse proxy cache +forever and to activate the `MediaProxyWarmingPolicy` MRF policy in Pleroma which will automatically fetch all media through the proxy +as soon as the post is received by your instance. + +## Nginx + +We should be using `proxy_store` here I think??? + +``` + location ~ ^/(media|proxy) { + proxy_cache pleroma_media_cache; + slice 1m; + proxy_cache_key $host$uri$is_args$args$slice_range; + proxy_set_header Range $slice_range; + proxy_http_version 1.1; + proxy_cache_valid 200 206 301 304 1h; + proxy_cache_lock on; + proxy_ignore_client_abort on; + proxy_buffering on; + chunked_transfer_encoding on; + proxy_ignore_headers Cache-Control; + proxy_hide_header Cache-Control; + proxy_pass http://127.0.0.1:4000; + } +``` + +## Pleroma + +Add to your `prod.secret.exs`: + +``` +config :pleroma, :instance, + rewrite_policy: [Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy] +``` From a1ad8dc34993445033595c8f52e0ee1815e5567d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Oct 2019 14:07:59 -0500 Subject: [PATCH 02/14] Leverage nginx proxy cache to store items with a 1 year TTL with no size limit. It does not purge items when they expire, but will only update them if the origin's copy has changed for some reason. If origin is offline/unavailable or gone forever it will still serve the cached copies. --- docs/administration/storing_remote_media.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/administration/storing_remote_media.md b/docs/administration/storing_remote_media.md index 7edda2753..0abb85a77 100644 --- a/docs/administration/storing_remote_media.md +++ b/docs/administration/storing_remote_media.md @@ -6,22 +6,25 @@ as soon as the post is received by your instance. ## Nginx -We should be using `proxy_store` here I think??? - ``` +proxy_cache_path /long/term/storage/path/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m + inactive=1y use_temp_path=off; + location ~ ^/(media|proxy) { proxy_cache pleroma_media_cache; slice 1m; proxy_cache_key $host$uri$is_args$args$slice_range; proxy_set_header Range $slice_range; proxy_http_version 1.1; - proxy_cache_valid 200 206 301 304 1h; + proxy_cache_valid 206 301 302 304 1h; + proxy_cache_valid 200 1y; proxy_cache_lock on; + proxy_cache_use_stale error timeout invalid_header updating; proxy_ignore_client_abort on; proxy_buffering on; chunked_transfer_encoding on; - proxy_ignore_headers Cache-Control; - proxy_hide_header Cache-Control; + proxy_ignore_headers Cache-Control Expires; + proxy_hide_header Cache-Control Expires; proxy_pass http://127.0.0.1:4000; } ``` From b9d164fb89af65c2aef83c2867c937ea39a9e995 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Oct 2019 14:12:01 -0500 Subject: [PATCH 03/14] Formatting --- docs/administration/storing_remote_media.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/administration/storing_remote_media.md b/docs/administration/storing_remote_media.md index 0abb85a77..74d333342 100644 --- a/docs/administration/storing_remote_media.md +++ b/docs/administration/storing_remote_media.md @@ -7,8 +7,8 @@ as soon as the post is received by your instance. ## Nginx ``` -proxy_cache_path /long/term/storage/path/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m - inactive=1y use_temp_path=off; + proxy_cache_path /long/term/storage/path/pleroma-media-cache levels=1:2 + keys_zone=pleroma_media_cache:10m inactive=1y use_temp_path=off; location ~ ^/(media|proxy) { proxy_cache pleroma_media_cache; From 47a551837ade9b5c5b7291c83bc3e787e9c6a17d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Oct 2019 15:13:42 -0500 Subject: [PATCH 04/14] Remove proxy_cache_lock suggestion --- docs/administration/storing_remote_media.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/administration/storing_remote_media.md b/docs/administration/storing_remote_media.md index 74d333342..619300e7e 100644 --- a/docs/administration/storing_remote_media.md +++ b/docs/administration/storing_remote_media.md @@ -18,7 +18,6 @@ as soon as the post is received by your instance. proxy_http_version 1.1; proxy_cache_valid 206 301 302 304 1h; proxy_cache_valid 200 1y; - proxy_cache_lock on; proxy_cache_use_stale error timeout invalid_header updating; proxy_ignore_client_abort on; proxy_buffering on; From 752d0c683357277f5926b7b7011b3f945a7610d1 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Oct 2019 15:14:04 -0500 Subject: [PATCH 05/14] Relocate to configuration subdir --- docs/{administration => configuration}/storing_remote_media.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{administration => configuration}/storing_remote_media.md (100%) diff --git a/docs/administration/storing_remote_media.md b/docs/configuration/storing_remote_media.md similarity index 100% rename from docs/administration/storing_remote_media.md rename to docs/configuration/storing_remote_media.md From 41db52729eee0158c90d69a8dfc0d87d2a866de0 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 14 May 2020 09:14:59 +0000 Subject: [PATCH 06/14] Apply suggestion to docs/configuration/storing_remote_media.md --- docs/configuration/storing_remote_media.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/storing_remote_media.md b/docs/configuration/storing_remote_media.md index 619300e7e..7e91fe7d9 100644 --- a/docs/configuration/storing_remote_media.md +++ b/docs/configuration/storing_remote_media.md @@ -1,7 +1,7 @@ # Storing Remote Media Pleroma does not store remote/federated media by default. The best way to achieve this is to change Nginx to keep its reverse proxy cache -forever and to activate the `MediaProxyWarmingPolicy` MRF policy in Pleroma which will automatically fetch all media through the proxy +for a year and to activate the `MediaProxyWarmingPolicy` MRF policy in Pleroma which will automatically fetch all media through the proxy as soon as the post is received by your instance. ## Nginx From af9dfdce6b502d3a33db7a496879dda56719f56e Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sun, 17 May 2020 08:46:43 +0300 Subject: [PATCH 07/14] MediaController OAuth scope assignments fix. Typo fix (`def get_media` instead of `def show`). --- .../mastodon_api/controllers/media_controller.ex | 6 ++++-- .../controllers/media_controller_test.exs | 14 +++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index a21233393..afa8b2ea2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -14,7 +14,8 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) - plug(OAuthScopesPlug, %{scopes: ["write:media"]}) + plug(OAuthScopesPlug, %{scopes: ["read:media"]} when action == :show) + plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action != :show) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.MediaOperation @@ -65,6 +66,7 @@ def update(%{assigns: %{user: user}, body_params: %{description: description}} = def update(conn, data), do: show(conn, data) + # TODO: clarify: is the access to non-owned objects granted intentionally? @doc "GET /api/v1/media/:id" def show(conn, %{id: id}) do with %Object{data: data, id: object_id} <- Object.get_by_id(id) do @@ -74,5 +76,5 @@ def show(conn, %{id: id}) do end end - def get_media(_conn, _data), do: {:error, :bad_request} + def show(_conn, _data), do: {:error, :bad_request} end diff --git a/test/web/mastodon_api/controllers/media_controller_test.exs b/test/web/mastodon_api/controllers/media_controller_test.exs index 7ba1727f2..98ec239b1 100644 --- a/test/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/web/mastodon_api/controllers/media_controller_test.exs @@ -9,9 +9,9 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - setup do: oauth_access(["write:media"]) - describe "Upload media" do + setup do: oauth_access(["write:media"]) + setup do image = %Plug.Upload{ content_type: "image/jpg", @@ -42,7 +42,7 @@ test "/api/v1/media", %{conn: conn, image: image} do assert object.data["actor"] == User.ap_id(conn.assigns[:user]) end - test "/api/v2/media", %{conn: conn, image: image} do + test "/api/v2/media", %{conn: conn, user: user, image: image} do desc = "Description of the image" response = @@ -53,6 +53,8 @@ test "/api/v2/media", %{conn: conn, image: image} do assert media_id = response["id"] + %{conn: conn} = oauth_access(["read:media"], user: user) + media = conn |> get("/api/v1/media/#{media_id}") @@ -62,11 +64,15 @@ test "/api/v2/media", %{conn: conn, image: image} do assert media["description"] == desc assert media["id"] object = Object.get_by_id(media["id"]) + + # TODO: clarify: if this EP allows access to non-owned objects, the following may be false: assert object.data["actor"] == User.ap_id(conn.assigns[:user]) end end describe "Update media description" do + setup do: oauth_access(["write:media"]) + setup %{user: actor} do file = %Plug.Upload{ content_type: "image/jpg", @@ -97,6 +103,8 @@ test "/api/v1/media/:id good request", %{conn: conn, object: object} do end describe "Get media by id" do + setup do: oauth_access(["read:media"]) + setup %{user: actor} do file = %Plug.Upload{ content_type: "image/jpg", From baef35bcc8685757b0039f76d2614bbb08e410f7 Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 17 May 2020 10:31:01 +0200 Subject: [PATCH 08/14] Authentication Plug: Update bcrypt password on login. --- lib/pleroma/plugs/authentication_plug.ex | 13 +++++++++++++ test/plugs/authentication_plug_test.exs | 18 ++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex index 2cdf6c951..7d7da6125 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/plugs/authentication_plug.ex @@ -30,6 +30,17 @@ def checkpw(_password, _password_hash) do false end + def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do + user + |> User.password_update_changeset(%{ + "password" => password, + "password_confirmation" => password + }) + |> Pleroma.Repo.update() + end + + def maybe_update_password(user, _), do: {:ok, user} + def call(%{assigns: %{user: %User{}}} = conn, _), do: conn def call( @@ -42,6 +53,8 @@ def call( _ ) do if checkpw(password, password_hash) do + {:ok, auth_user} = maybe_update_password(auth_user, password) + conn |> assign(:user, auth_user) |> OAuthScopesPlug.skip_plug() diff --git a/test/plugs/authentication_plug_test.exs b/test/plugs/authentication_plug_test.exs index c8ede71c0..2c793b29a 100644 --- a/test/plugs/authentication_plug_test.exs +++ b/test/plugs/authentication_plug_test.exs @@ -11,6 +11,7 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do alias Pleroma.User import ExUnit.CaptureLog + import Pleroma.Factory setup %{conn: conn} do user = %User{ @@ -50,16 +51,21 @@ test "with a correct password in the credentials, " <> assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) end - test "with a wrong password in the credentials, it does nothing", %{conn: conn} do + test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do + user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123")) + assert "$2" <> _ = user.password_hash + conn = conn - |> assign(:auth_credentials, %{password: "wrong"}) - - ret_conn = - conn + |> assign(:auth_user, user) + |> assign(:auth_credentials, %{password: "123"}) |> AuthenticationPlug.call(%{}) - assert conn == ret_conn + assert conn.assigns.user.id == conn.assigns.auth_user.id + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + + user = User.get_by_id(user.id) + assert "$pbkdf2" <> _ = user.password_hash end describe "checkpw/2" do From bfdd90f6d7c9bb85e572033070d6fa7efda8aeac Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 17 May 2020 11:40:25 +0200 Subject: [PATCH 09/14] AuthenticationPlug: Also update crypt passwords. --- lib/pleroma/plugs/authentication_plug.ex | 12 ++++++++++-- test/plugs/authentication_plug_test.exs | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex index 7d7da6125..057ea42f1 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/plugs/authentication_plug.ex @@ -31,6 +31,16 @@ def checkpw(_password, _password_hash) do end def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do + do_update_password(user, password) + end + + def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do + do_update_password(user, password) + end + + def maybe_update_password(user, _), do: {:ok, user} + + defp do_update_password(user, password) do user |> User.password_update_changeset(%{ "password" => password, @@ -39,8 +49,6 @@ def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do |> Pleroma.Repo.update() end - def maybe_update_password(user, _), do: {:ok, user} - def call(%{assigns: %{user: %User{}}} = conn, _), do: conn def call( diff --git a/test/plugs/authentication_plug_test.exs b/test/plugs/authentication_plug_test.exs index 2c793b29a..3c70c1747 100644 --- a/test/plugs/authentication_plug_test.exs +++ b/test/plugs/authentication_plug_test.exs @@ -68,6 +68,26 @@ test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do assert "$pbkdf2" <> _ = user.password_hash end + test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do + user = + insert(:user, + password_hash: + "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" + ) + + conn = + conn + |> assign(:auth_user, user) + |> assign(:auth_credentials, %{password: "password"}) + |> AuthenticationPlug.call(%{}) + + assert conn.assigns.user.id == conn.assigns.auth_user.id + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + + user = User.get_by_id(user.id) + assert "$pbkdf2" <> _ = user.password_hash + end + describe "checkpw/2" do test "check pbkdf2 hash" do hash = From 8bfd9710ae70204b29e184f08d78b95a2f81ad6c Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 17 May 2020 11:53:17 +0200 Subject: [PATCH 10/14] Pleroma Authenticator: Also update passwords here. --- lib/pleroma/web/auth/pleroma_authenticator.ex | 3 ++- test/web/auth/pleroma_authenticator_test.exs | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index a8f554aa3..200ca03dc 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -16,7 +16,8 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do def get_user(%Plug.Conn{} = conn) do with {:ok, {name, password}} <- fetch_credentials(conn), {_, %User{} = user} <- {:user, fetch_user(name)}, - {_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)} do + {_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)}, + {:ok, user} <- AuthenticationPlug.maybe_update_password(user, password) do {:ok, user} else {:error, _reason} = error -> error diff --git a/test/web/auth/pleroma_authenticator_test.exs b/test/web/auth/pleroma_authenticator_test.exs index 5a421e5ed..731bd5932 100644 --- a/test/web/auth/pleroma_authenticator_test.exs +++ b/test/web/auth/pleroma_authenticator_test.exs @@ -15,11 +15,16 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do {:ok, [user: user, name: name, password: password]} end - test "get_user/authorization", %{user: user, name: name, password: password} do + test "get_user/authorization", %{name: name, password: password} do + name = name <> "1" + user = insert(:user, nickname: name, password_hash: Bcrypt.hash_pwd_salt(password)) + params = %{"authorization" => %{"name" => name, "password" => password}} res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) - assert {:ok, user} == res + assert {:ok, returned_user} = res + assert returned_user.id == user.id + assert "$pbkdf2" <> _ = returned_user.password_hash end test "get_user/authorization with invalid password", %{name: name} do From 9b765652649f8b6110bd70aa90b148a90057ff6a Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 18 May 2020 09:51:53 +0300 Subject: [PATCH 11/14] MediaController: enforced owner-only access in :show action. Improved error response on denied access (now 403). Adjusted tests. --- lib/pleroma/object.ex | 15 +++++++++----- .../controllers/fallback_controller.ex | 4 ++++ .../controllers/media_controller.ex | 8 ++++---- .../controllers/media_controller_test.exs | 20 ++++++++++++++----- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index e678fd415..ab16bf2db 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -138,12 +138,17 @@ def normalize(ap_id, true, options) when is_binary(ap_id) do def normalize(_, _, _), do: nil - # Owned objects can only be mutated by their owner - def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}), - do: actor == ap_id + # Owned objects can only be accessed by their owner + def authorize_access(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}) do + if actor == ap_id do + :ok + else + {:error, :forbidden} + end + end - # Legacy objects can be mutated by anybody - def authorize_mutation(%Object{}, %User{}), do: true + # Legacy objects can be accessed by anybody + def authorize_access(%Object{}, %User{}), do: :ok @spec get_cached_by_ap_id(String.t()) :: Object.t() | nil def get_cached_by_ap_id(ap_id) do diff --git a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex index 0a257f604..8af557b61 100644 --- a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex @@ -20,6 +20,10 @@ def call(conn, {:error, :not_found}) do render_error(conn, :not_found, "Record not found") end + def call(conn, {:error, :forbidden}) do + render_error(conn, :forbidden, "Access denied") + end + def call(conn, {:error, error_message}) do conn |> put_status(:bad_request) diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index afa8b2ea2..513de279f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -56,7 +56,7 @@ def create2(_conn, _data), do: {:error, :bad_request} @doc "PUT /api/v1/media/:id" def update(%{assigns: %{user: user}, body_params: %{description: description}} = conn, %{id: id}) do with %Object{} = object <- Object.get_by_id(id), - true <- Object.authorize_mutation(object, user), + :ok <- Object.authorize_access(object, user), {:ok, %Object{data: data}} <- Object.update_data(object, %{"name" => description}) do attachment_data = Map.put(data, "id", object.id) @@ -66,10 +66,10 @@ def update(%{assigns: %{user: user}, body_params: %{description: description}} = def update(conn, data), do: show(conn, data) - # TODO: clarify: is the access to non-owned objects granted intentionally? @doc "GET /api/v1/media/:id" - def show(conn, %{id: id}) do - with %Object{data: data, id: object_id} <- Object.get_by_id(id) do + def show(%{assigns: %{user: user}} = conn, %{id: id}) do + with %Object{data: data, id: object_id} = object <- Object.get_by_id(id), + :ok <- Object.authorize_access(object, user) do attachment_data = Map.put(data, "id", object_id) render(conn, "attachment.json", %{attachment: attachment_data}) diff --git a/test/web/mastodon_api/controllers/media_controller_test.exs b/test/web/mastodon_api/controllers/media_controller_test.exs index 98ec239b1..906fd940f 100644 --- a/test/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/web/mastodon_api/controllers/media_controller_test.exs @@ -63,10 +63,9 @@ test "/api/v2/media", %{conn: conn, user: user, image: image} do assert media["type"] == "image" assert media["description"] == desc assert media["id"] - object = Object.get_by_id(media["id"]) - # TODO: clarify: if this EP allows access to non-owned objects, the following may be false: - assert object.data["actor"] == User.ap_id(conn.assigns[:user]) + object = Object.get_by_id(media["id"]) + assert object.data["actor"] == user.ap_id end end @@ -102,7 +101,7 @@ test "/api/v1/media/:id good request", %{conn: conn, object: object} do end end - describe "Get media by id" do + describe "Get media by id (/api/v1/media/:id)" do setup do: oauth_access(["read:media"]) setup %{user: actor} do @@ -122,7 +121,7 @@ test "/api/v1/media/:id good request", %{conn: conn, object: object} do [object: object] end - test "/api/v1/media/:id", %{conn: conn, object: object} do + test "it returns media object when requested by owner", %{conn: conn, object: object} do media = conn |> get("/api/v1/media/#{object.id}") @@ -132,5 +131,16 @@ test "/api/v1/media/:id", %{conn: conn, object: object} do assert media["type"] == "image" assert media["id"] end + + test "it returns 403 if media object requested by non-owner", %{object: object, user: user} do + %{conn: conn, user: other_user} = oauth_access(["read:media"]) + + assert object.data["actor"] == user.ap_id + refute user.id == other_user.id + + conn + |> get("/api/v1/media/#{object.id}") + |> json_response(403) + end end end From 7b500d6b4fdad59c7242dc41646bd0bb5cc04e67 Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 18 May 2020 13:56:58 +0200 Subject: [PATCH 12/14] Load Testing: adjust to new CommonAPI.post format. --- benchmarks/load_testing/activities.ex | 41 +++++++++++++-------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/benchmarks/load_testing/activities.ex b/benchmarks/load_testing/activities.ex index 482e42fc1..ff0d481a8 100644 --- a/benchmarks/load_testing/activities.ex +++ b/benchmarks/load_testing/activities.ex @@ -123,7 +123,7 @@ def generate_tagged_activities(opts \\ []) do Enum.each(1..activity_count, fn _ -> random = :rand.uniform() i = Enum.find_index(intervals, fn {lower, upper} -> lower <= random && upper > random end) - CommonAPI.post(Enum.random(users), %{"status" => "a post with the tag #tag_#{i}"}) + CommonAPI.post(Enum.random(users), %{status: "a post with the tag #tag_#{i}"}) end) end @@ -137,8 +137,8 @@ defp generate_long_thread(visibility, user, friends, non_friends, _opts) do {:ok, activity} = CommonAPI.post(user, %{ - "status" => "Start of #{visibility} long thread", - "visibility" => visibility + status: "Start of #{visibility} long thread", + visibility: visibility }) Agent.update(:benchmark_state, fn state -> @@ -186,7 +186,7 @@ defp insert_activity("simple", visibility, group, user, friends, non_friends, _o {:ok, _activity} = group |> get_actor(user, friends, non_friends) - |> CommonAPI.post(%{"status" => "Simple status", "visibility" => visibility}) + |> CommonAPI.post(%{status: "Simple status", visibility: visibility}) end defp insert_activity("emoji", visibility, group, user, friends, non_friends, _opts) do @@ -194,8 +194,8 @@ defp insert_activity("emoji", visibility, group, user, friends, non_friends, _op group |> get_actor(user, friends, non_friends) |> CommonAPI.post(%{ - "status" => "Simple status with emoji :firefox:", - "visibility" => visibility + status: "Simple status with emoji :firefox:", + visibility: visibility }) end @@ -213,8 +213,8 @@ defp insert_activity("mentions", visibility, group, user, friends, non_friends, group |> get_actor(user, friends, non_friends) |> CommonAPI.post(%{ - "status" => Enum.join(user_mentions, ", ") <> " simple status with mentions", - "visibility" => visibility + status: Enum.join(user_mentions, ", ") <> " simple status with mentions", + visibility: visibility }) end @@ -236,8 +236,8 @@ defp insert_activity("hell_thread", visibility, group, user, friends, non_friend group |> get_actor(user, friends, non_friends) |> CommonAPI.post(%{ - "status" => mentions <> " hell thread status", - "visibility" => visibility + status: mentions <> " hell thread status", + visibility: visibility }) end @@ -262,9 +262,9 @@ defp insert_activity("attachment", visibility, group, user, friends, non_friends {:ok, _activity} = CommonAPI.post(actor, %{ - "status" => "Post with attachment", - "visibility" => visibility, - "media_ids" => [object.id] + status: "Post with attachment", + visibility: visibility, + media_ids: [object.id] }) end @@ -272,7 +272,7 @@ defp insert_activity("tag", visibility, group, user, friends, non_friends, _opts {:ok, _activity} = group |> get_actor(user, friends, non_friends) - |> CommonAPI.post(%{"status" => "Status with #tag", "visibility" => visibility}) + |> CommonAPI.post(%{status: "Status with #tag", visibility: visibility}) end defp insert_activity("like", visibility, group, user, friends, non_friends, opts) do @@ -312,8 +312,7 @@ defp insert_activity("simple_thread", visibility, group, user, friends, non_frie actor = get_actor(group, user, friends, non_friends) tasks = get_reply_tasks(visibility, group) - {:ok, activity} = - CommonAPI.post(user, %{"status" => "Simple status", "visibility" => visibility}) + {:ok, activity} = CommonAPI.post(user, %{status: "Simple status", visibility: visibility}) acc = {activity.id, ["@" <> actor.nickname, "reply to status"]} insert_replies(tasks, visibility, user, friends, non_friends, acc) @@ -336,8 +335,8 @@ defp insert_activity("simple_thread", "direct", group, user, friends, non_friend {:ok, activity} = CommonAPI.post(actor, %{ - "status" => Enum.join(data, ", ") <> "simple status", - "visibility" => "direct" + status: Enum.join(data, ", ") <> "simple status", + visibility: "direct" }) acc = {activity.id, ["@" <> user.nickname | data] ++ ["reply to status"]} @@ -527,9 +526,9 @@ defp insert_direct_replies(tasks, user, list, acc) do defp insert_reply(actor, data, activity_id, visibility) do {:ok, reply} = CommonAPI.post(actor, %{ - "status" => Enum.join(data, ", "), - "visibility" => visibility, - "in_reply_to_status_id" => activity_id + status: Enum.join(data, ", "), + visibility: visibility, + in_reply_to_status_id: activity_id }) {reply.id, ["@" <> actor.nickname | data]} From 215daabdb4441ca6620366cc06c6827c8dc69bc5 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Mon, 18 May 2020 15:15:51 +0300 Subject: [PATCH 13/14] copyright update --- lib/pleroma/mfa.ex | 2 +- lib/pleroma/mfa/backup_codes.ex | 2 +- lib/pleroma/mfa/changeset.ex | 2 +- lib/pleroma/mfa/settings.ex | 2 +- lib/pleroma/mfa/token.ex | 2 +- lib/pleroma/mfa/totp.ex | 2 +- lib/pleroma/web/auth/totp_authenticator.ex | 2 +- lib/pleroma/web/oauth/mfa_controller.ex | 2 +- lib/pleroma/web/oauth/mfa_view.ex | 2 +- lib/pleroma/web/oauth/token/clean_worker.ex | 2 +- .../controllers/two_factor_authentication_controller.ex | 2 +- test/web/auth/pleroma_authenticator_test.exs | 2 +- test/web/auth/totp_authenticator_test.exs | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/pleroma/mfa.ex b/lib/pleroma/mfa.ex index 2b77f5426..01b743f4f 100644 --- a/lib/pleroma/mfa.ex +++ b/lib/pleroma/mfa.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA do diff --git a/lib/pleroma/mfa/backup_codes.ex b/lib/pleroma/mfa/backup_codes.ex index 2b5ec34f8..9875310ff 100644 --- a/lib/pleroma/mfa/backup_codes.ex +++ b/lib/pleroma/mfa/backup_codes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.BackupCodes do diff --git a/lib/pleroma/mfa/changeset.ex b/lib/pleroma/mfa/changeset.ex index 9b020aa8e..77c4fa202 100644 --- a/lib/pleroma/mfa/changeset.ex +++ b/lib/pleroma/mfa/changeset.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Changeset do diff --git a/lib/pleroma/mfa/settings.ex b/lib/pleroma/mfa/settings.ex index 2764b889c..de6e2228f 100644 --- a/lib/pleroma/mfa/settings.ex +++ b/lib/pleroma/mfa/settings.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Settings do diff --git a/lib/pleroma/mfa/token.ex b/lib/pleroma/mfa/token.ex index 25ff7fb29..0b2449971 100644 --- a/lib/pleroma/mfa/token.ex +++ b/lib/pleroma/mfa/token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Token do diff --git a/lib/pleroma/mfa/totp.ex b/lib/pleroma/mfa/totp.ex index 1407afc57..d2ea2b3aa 100644 --- a/lib/pleroma/mfa/totp.ex +++ b/lib/pleroma/mfa/totp.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.TOTP do diff --git a/lib/pleroma/web/auth/totp_authenticator.ex b/lib/pleroma/web/auth/totp_authenticator.ex index ce8a76219..1794e407c 100644 --- a/lib/pleroma/web/auth/totp_authenticator.ex +++ b/lib/pleroma/web/auth/totp_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.TOTPAuthenticator do diff --git a/lib/pleroma/web/oauth/mfa_controller.ex b/lib/pleroma/web/oauth/mfa_controller.ex index e52cccd85..53e19f82e 100644 --- a/lib/pleroma/web/oauth/mfa_controller.ex +++ b/lib/pleroma/web/oauth/mfa_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.MFAController do diff --git a/lib/pleroma/web/oauth/mfa_view.ex b/lib/pleroma/web/oauth/mfa_view.ex index e88e7066b..41d5578dc 100644 --- a/lib/pleroma/web/oauth/mfa_view.ex +++ b/lib/pleroma/web/oauth/mfa_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.MFAView do diff --git a/lib/pleroma/web/oauth/token/clean_worker.ex b/lib/pleroma/web/oauth/token/clean_worker.ex index 2c3bb9ded..e3aa4eb7e 100644 --- a/lib/pleroma/web/oauth/token/clean_worker.ex +++ b/lib/pleroma/web/oauth/token/clean_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.CleanWorker do diff --git a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex index eb9989cdf..b86791d09 100644 --- a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do diff --git a/test/web/auth/pleroma_authenticator_test.exs b/test/web/auth/pleroma_authenticator_test.exs index 731bd5932..1ba0dfecc 100644 --- a/test/web/auth/pleroma_authenticator_test.exs +++ b/test/web/auth/pleroma_authenticator_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do diff --git a/test/web/auth/totp_authenticator_test.exs b/test/web/auth/totp_authenticator_test.exs index e502e0ae8..84d4cd840 100644 --- a/test/web/auth/totp_authenticator_test.exs +++ b/test/web/auth/totp_authenticator_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do From 5735b5c8beccf7c5ff1cd6586d7e4bb6bc8bda12 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 18 May 2020 19:00:00 +0300 Subject: [PATCH 14/14] Fixed missing support for `with_muted` param in direct timeline. --- lib/pleroma/web/api_spec/operations/timeline_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index cb9d75841..8e19bace7 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -43,7 +43,7 @@ def direct_operation do description: "View statuses with a “direct” privacy, from your account or in your notifications", deprecated: true, - parameters: pagination_params(), + parameters: [with_muted_param() | pagination_params()], security: [%{"oAuth" => ["read:statuses"]}], operationId: "TimelineController.direct", responses: %{