From 6c396fcab49f5de8b3f73a8353748d3606687966 Mon Sep 17 00:00:00 2001 From: ilja Date: Mon, 6 Mar 2023 12:04:35 +0100 Subject: [PATCH 01/15] Remove "default" image description When no image description is filled in, Pleroma allowed fallbacks. Those were (based on a setting) either the filename, or a fixed description. Neither are good options for image descriptions imo, so here we remove this. Note that there's two tests removed who supposedly tested something else. But examining closer, they didn't seem to test what they claimed to test, so I removed them rather than try to "fix" them. --- CHANGELOG.md | 1 + config/config.exs | 1 - config/test.exs | 3 +-- docs/docs/configuration/cheatsheet.md | 1 - lib/pleroma/upload.ex | 11 +--------- test/pleroma/upload_test.exs | 17 ++------------ .../web/activity_pub/activity_pub_test.exs | 22 ------------------- 7 files changed, 5 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 859a09e7d..b451f297f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Removed - Possibility of using the `style` parameter on `span` elements. This will break certain MFM parameters. +- Option for "default" image description. ## 2023.02 diff --git a/config/config.exs b/config/config.exs index 5eaa8ce76..4d8fd52c4 100644 --- a/config/config.exs +++ b/config/config.exs @@ -65,7 +65,6 @@ link_name: false, proxy_remote: false, filename_display_max_length: 30, - default_description: nil, base_url: nil config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads" diff --git a/config/test.exs b/config/test.exs index 3056dbd03..4448eeb73 100644 --- a/config/test.exs +++ b/config/test.exs @@ -23,8 +23,7 @@ config :pleroma, Pleroma.Upload, filters: [], - link_name: false, - default_description: :filename + link_name: false config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads" diff --git a/docs/docs/configuration/cheatsheet.md b/docs/docs/configuration/cheatsheet.md index 4e84b9a44..1c4d9ec5d 100644 --- a/docs/docs/configuration/cheatsheet.md +++ b/docs/docs/configuration/cheatsheet.md @@ -562,7 +562,6 @@ the source code is here: [kocaptcha](https://github.com/koto-bank/kocaptcha). Th * `proxy_remote`: If you're using a remote uploader, Akkoma will proxy media requests instead of redirecting to it. * `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation. * `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30. -* `default_description`: Sets which default description an image has if none is set explicitly. Options: nil (default) - Don't set a default, :filename - use the filename of the file, a string (e.g. "attachment") - Use this string !!! warning `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`. diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 3b5419db7..2f65540be 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -65,15 +65,6 @@ defmodule Pleroma.Upload do } defstruct [:id, :name, :tempfile, :content_type, :width, :height, :blurhash, :path] - defp get_description(opts, upload) do - case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do - {description, _} when is_binary(description) -> description - {_, :filename} -> upload.name - {_, str} when is_binary(str) -> str - _ -> "" - end - end - @spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()} @doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct." def store(upload, opts \\ []) do @@ -82,7 +73,7 @@ def store(upload, opts \\ []) do with {:ok, upload} <- prepare_upload(upload, opts), upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, {:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload), - description = get_description(opts, upload), + description = Map.get(opts, :description) || "", {_, true} <- {:description_limit, String.length(description) <= Pleroma.Config.get([:instance, :description_limit])}, diff --git a/test/pleroma/upload_test.exs b/test/pleroma/upload_test.exs index 8f242630f..ad6065b43 100644 --- a/test/pleroma/upload_test.exs +++ b/test/pleroma/upload_test.exs @@ -54,7 +54,7 @@ test "it returns file" do assert result == %{ "id" => result["id"], - "name" => "image.jpg", + "name" => "", "type" => "Document", "mediaType" => "image/jpeg", "url" => [ @@ -154,19 +154,6 @@ test "copies the file to the configured folder with deduping" do "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg" end - test "copies the file to the configured folder without deduping" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "image/jpeg", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image.jpg" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "an [image.jpg" - end - test "fixes incorrect content type when base64 is given" do params = %{ img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}" @@ -184,7 +171,7 @@ test "adds extension when base64 is given" do } {:ok, data} = Upload.store(params) - assert String.ends_with?(data["name"], ".jpg") + assert String.ends_with?(List.first(data["url"])["href"], ".jpg") end test "copies the file to the configured folder with anonymizing filename" do diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 17c52fc91..20435d149 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1308,28 +1308,6 @@ test "sets a description if given", %{test_file: file} do assert object.data["name"] == "a cool file" end - test "it sets the default description depending on the configuration", %{test_file: file} do - clear_config([Pleroma.Upload, :default_description]) - - clear_config([Pleroma.Upload, :default_description], nil) - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "" - - clear_config([Pleroma.Upload, :default_description], :filename) - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "an_image.jpg" - - clear_config([Pleroma.Upload, :default_description], "unnamed attachment") - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "unnamed attachment" - end - - test "copies the file to the configured folder", %{test_file: file} do - clear_config([Pleroma.Upload, :default_description], :filename) - {:ok, %Object{} = object} = ActivityPub.upload(file) - assert object.data["name"] == "an_image.jpg" - end - test "works with base64 encoded images" do file = %{ img: data_uri() From 3d964a997033ea65e97cbd08b6549f9cdf445fc9 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 12 Mar 2023 23:24:07 +0000 Subject: [PATCH 02/15] Add frontend preference route --- config/config.exs | 3 + .../frontend_settings_controller.ex | 28 +++++++++ .../operations/frontend_settings_operation.ex | 59 +++++++++++++++++-- lib/pleroma/web/router.ex | 16 +++++ 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/config/config.exs b/config/config.exs index 5eaa8ce76..8ed3c9cd9 100644 --- a/config/config.exs +++ b/config/config.exs @@ -745,6 +745,9 @@ primary: %{"name" => "pleroma-fe", "ref" => "stable"}, admin: %{"name" => "admin-fe", "ref" => "stable"}, mastodon: %{"name" => "mastodon-fe", "ref" => "akkoma"}, + pickable: [ + "pleroma-fe/stable" + ], swagger: %{ "name" => "swagger-ui", "ref" => "stable", diff --git a/lib/pleroma/web/akkoma_api/controllers/frontend_settings_controller.ex b/lib/pleroma/web/akkoma_api/controllers/frontend_settings_controller.ex index c13ff9096..307d35643 100644 --- a/lib/pleroma/web/akkoma_api/controllers/frontend_settings_controller.ex +++ b/lib/pleroma/web/akkoma_api/controllers/frontend_settings_controller.ex @@ -5,6 +5,16 @@ defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsController do alias Pleroma.Akkoma.FrontendSettingsProfile @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} + + plug( + OAuthScopesPlug, + @unauthenticated_access + when action in [ + :available_frontends, + :update_preferred_frontend + ] + ) + plug( OAuthScopesPlug, %{@unauthenticated_access | scopes: ["read:accounts"]} @@ -93,4 +103,22 @@ def update_profile(%{body_params: %{settings: settings, version: version}} = con |> json(profile.settings) end end + + @doc "GET /api/v1/akkoma/preferred_frontend/available" + def available_frontends(conn, _params) do + available = Pleroma.Config.get([:frontends, :pickable]) + + conn + |> json(available) + end + + @doc "PUT /api/v1/akkoma/preferred_frontend" + def update_preferred_frontend( + %{body_params: %{frontend_name: preferred_frontend}} = conn, + _params + ) do + conn + |> put_resp_cookie("preferred_frontend", preferred_frontend) + |> json(%{frontend_name: preferred_frontend}) + end end diff --git a/lib/pleroma/web/api_spec/operations/frontend_settings_operation.ex b/lib/pleroma/web/api_spec/operations/frontend_settings_operation.ex index 40e81ad55..867a751b3 100644 --- a/lib/pleroma/web/api_spec/operations/frontend_settings_operation.ex +++ b/lib/pleroma/web/api_spec/operations/frontend_settings_operation.ex @@ -12,7 +12,7 @@ def open_api_operation(action) do @spec list_profiles_operation() :: Operation.t() def list_profiles_operation() do %Operation{ - tags: ["Retrieve frontend setting profiles"], + tags: ["Frontends"], summary: "Frontend Settings Profiles", description: "List frontend setting profiles", operationId: "AkkomaAPI.FrontendSettingsController.list_profiles", @@ -37,7 +37,7 @@ def list_profiles_operation() do @spec get_profile_operation() :: Operation.t() def get_profile_operation() do %Operation{ - tags: ["Retrieve frontend setting profile"], + tags: ["Frontends"], summary: "Frontend Settings Profile", description: "Get frontend setting profile", operationId: "AkkomaAPI.FrontendSettingsController.get_profile", @@ -60,7 +60,7 @@ def get_profile_operation() do @spec delete_profile_operation() :: Operation.t() def delete_profile_operation() do %Operation{ - tags: ["Delete frontend setting profile"], + tags: ["Frontends"], summary: "Delete frontend Settings Profile", description: "Delete frontend setting profile", operationId: "AkkomaAPI.FrontendSettingsController.delete_profile", @@ -76,7 +76,7 @@ def delete_profile_operation() do @spec update_profile_operation() :: Operation.t() def update_profile_operation() do %Operation{ - tags: ["Update frontend setting profile"], + tags: ["Frontends"], summary: "Frontend Settings Profile", description: "Update frontend setting profile", operationId: "AkkomaAPI.FrontendSettingsController.update_profile_operation", @@ -90,6 +90,57 @@ def update_profile_operation() do } end + def available_frontends_operation() do + %Operation{ + tags: ["Frontends"], + summary: "Frontend Settings Profiles", + description: "List frontend setting profiles", + operationId: "AkkomaAPI.FrontendSettingsController.available_frontends", + responses: %{ + 200 => + Operation.response("Frontends", "application/json", %Schema{ + type: :array, + items: %Schema{ + type: :string + } + }) + } + } + end + + def update_preferred_frontend_operation() do + %Operation{ + tags: ["Frontends"], + summary: "Frontend Settings Profiles", + description: "List frontend setting profiles", + operationId: "AkkomaAPI.FrontendSettingsController.available_frontends", + requestBody: + request_body( + "Frontend", + %Schema{ + type: :object, + required: [:frontend_name], + properties: %{ + frontend_name: %Schema{ + type: :string, + description: "Frontend name" + } + } + }, + required: true + ), + responses: %{ + 200 => + Operation.response("Frontends", "application/json", %Schema{ + type: :array, + items: %Schema{ + type: :string + } + }) + } + } + end + def frontend_name_param do Operation.parameter(:frontend_name, :path, :string, "Frontend name", example: "pleroma-fe", diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index faaf3d679..3db8ddab7 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -466,6 +466,22 @@ defmodule Pleroma.Web.Router do put("/statuses/:id/emoji_reactions/:emoji", EmojiReactionController, :create) end + scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do + pipe_through(:api) + + get( + "/api/v1/akkoma/preferred_frontend/available", + FrontendSettingsController, + :available_frontends + ) + + put( + "/api/v1/akkoma/preferred_frontend", + FrontendSettingsController, + :update_preferred_frontend + ) + end + scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do pipe_through(:authenticated_api) get("/metrics", MetricsController, :show) From 643b8c5f152bb71ef11074edd4a78f1405f360da Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sun, 12 Mar 2023 23:59:10 +0000 Subject: [PATCH 03/15] ensure we send the right files for preferred fe --- .../web/fallback/redirect_controller.ex | 11 ++--- lib/pleroma/web/plugs/frontend_static.ex | 45 ++++++++++++++++--- lib/pleroma/web/plugs/instance_static.ex | 4 +- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex index 49f659cf0..2e57fa426 100644 --- a/lib/pleroma/web/fallback/redirect_controller.ex +++ b/lib/pleroma/web/fallback/redirect_controller.ex @@ -20,7 +20,7 @@ def api_not_implemented(conn, _params) do def redirector(conn, _params, code \\ 200) do conn |> put_resp_content_type("text/html") - |> send_file(code, index_file_path()) + |> send_file(code, index_file_path(conn)) end def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do @@ -33,7 +33,7 @@ def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} end def redirector_with_meta(conn, params) do - {:ok, index_content} = File.read(index_file_path()) + {:ok, index_content} = File.read(index_file_path(conn)) tags = build_tags(conn, params) preloads = preload_data(conn, params) @@ -53,7 +53,7 @@ def redirector_with_preload(conn, %{"path" => ["pleroma", "admin"]}) do end def redirector_with_preload(conn, params) do - {:ok, index_content} = File.read(index_file_path()) + {:ok, index_content} = File.read(index_file_path(conn)) preloads = preload_data(conn, params) tags = Metadata.build_static_tags(params) title = "#{Pleroma.Config.get([:instance, :name])}" @@ -77,8 +77,9 @@ def empty(conn, _params) do |> text("") end - defp index_file_path do - Pleroma.Web.Plugs.InstanceStatic.file_path("index.html") + defp index_file_path(conn) do + frontend_type = Pleroma.Web.Plugs.FrontendStatic.preferred_or_fallback(conn, :primary) + Pleroma.Web.Plugs.InstanceStatic.file_path("index.html", frontend_type) end defp build_tags(conn, params) do diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex index 40f51e149..62283353e 100644 --- a/lib/pleroma/web/plugs/frontend_static.ex +++ b/lib/pleroma/web/plugs/frontend_static.ex @@ -5,17 +5,23 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do require Pleroma.Constants + @frontend_cookie_name "preferred_frontend" + @moduledoc """ This is a shim to call `Plug.Static` but with runtime `from` configuration`. It dispatches to the different frontends. """ @behaviour Plug - def file_path(path, frontend_type \\ :primary) do - if configuration = Pleroma.Config.get([:frontends, frontend_type]) do - instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static") + defp instance_static_path do + Pleroma.Config.get([:instance, :static_dir], "instance/static") + end + def file_path(path, frontend_type \\ :primary) + + def file_path(path, frontend_type) when is_atom(frontend_type) do + if configuration = Pleroma.Config.get([:frontends, frontend_type]) do Path.join([ - instance_static_path, + instance_static_path(), "frontends", configuration["name"], configuration["ref"], @@ -26,6 +32,15 @@ def file_path(path, frontend_type \\ :primary) do end end + def file_path(path, frontend_type) when is_binary(frontend_type) do + Path.join([ + instance_static_path(), + "frontends", + frontend_type, + path + ]) + end + def init(opts) do opts |> Keyword.put(:from, "__unconfigured_frontend_static_plug") @@ -38,7 +53,8 @@ def call(conn, opts) do with false <- api_route?(conn.path_info), false <- invalid_path?(conn.path_info), true <- enabled?(opts[:if]), - frontend_type <- Map.get(opts, :frontend_type, :primary), + fallback_frontend_type <- Map.get(opts, :frontend_type, :primary), + frontend_type <- preferred_or_fallback(conn, fallback_frontend_type), path when not is_nil(path) <- file_path("", frontend_type) do call_static(conn, opts, path) else @@ -47,6 +63,24 @@ def call(conn, opts) do end end + def preferred_frontend(conn) do + %{req_cookies: cookies} = + conn + |> Plug.Conn.fetch_cookies() + + Map.get(cookies, @frontend_cookie_name) + end + + def preferred_or_fallback(conn, fallback) do + case preferred_frontend(conn) do + nil -> + fallback + + frontend -> + frontend + end + end + defp enabled?(if_opt) when is_function(if_opt), do: if_opt.() defp enabled?(true), do: true defp enabled?(_), do: false @@ -68,6 +102,7 @@ defp api_route?([h | t]) do defp call_static(conn, opts, from) do opts = Map.put(opts, :from, from) + IO.inspect(opts, label: "opts") Plug.Static.call(conn, opts) end end diff --git a/lib/pleroma/web/plugs/instance_static.ex b/lib/pleroma/web/plugs/instance_static.ex index 723b25679..5f9a6ee83 100644 --- a/lib/pleroma/web/plugs/instance_static.ex +++ b/lib/pleroma/web/plugs/instance_static.ex @@ -12,11 +12,11 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do """ @behaviour Plug - def file_path(path) do + def file_path(path, frontend_type \\ :primary) do instance_path = Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path) - frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, :primary) + frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, frontend_type) (File.exists?(instance_path) && instance_path) || (frontend_path && File.exists?(frontend_path) && frontend_path) || From de64c6c54aaacc4123031f2e3d5bfb9fc9c517fe Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Tue, 28 Mar 2023 12:44:52 +0100 Subject: [PATCH 04/15] add selection UI --- config/description.exs | 6 ++++++ .../controllers/frontend_switcher.ex | 20 +++++++++++++++++++ .../web/akkoma_api/views/frontend_switcher.ex | 3 +++ lib/pleroma/web/plugs/frontend_static.ex | 8 ++++++-- lib/pleroma/web/router.ex | 7 +++++++ .../frontend_switcher/switch.html.eex | 7 +++++++ 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 lib/pleroma/web/akkoma_api/controllers/frontend_switcher.ex create mode 100644 lib/pleroma/web/akkoma_api/views/frontend_switcher.ex create mode 100644 lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex diff --git a/config/description.exs b/config/description.exs index 2a2d70a7b..75fd23566 100644 --- a/config/description.exs +++ b/config/description.exs @@ -3148,6 +3148,12 @@ description: "A map containing available frontends and parameters for their installation.", children: frontend_options + }, + %{ + key: :pickable, + type: {:list, :string}, + description: + "A list containing all frontends users can pick as their preference, format is :name/:ref, e.g pleroma-fe/stable." } ] }, diff --git a/lib/pleroma/web/akkoma_api/controllers/frontend_switcher.ex b/lib/pleroma/web/akkoma_api/controllers/frontend_switcher.ex new file mode 100644 index 000000000..2095db4b5 --- /dev/null +++ b/lib/pleroma/web/akkoma_api/controllers/frontend_switcher.ex @@ -0,0 +1,20 @@ +defmodule Pleroma.Web.AkkomaAPI.FrontendSwitcherController do + use Pleroma.Web, :controller + alias Pleroma.Config + + @doc "GET /akkoma/frontend" + def switch(conn, _params) do + pickable = Config.get([:frontends, :pickable], []) + + conn + |> put_view(Pleroma.Web.AkkomaAPI.FrontendSwitcherView) + |> render("switch.html", choices: pickable) + end + + @doc "POST /akkoma/frontend" + def do_switch(conn, params) do + conn + |> put_resp_cookie("preferred_frontend", params["frontend"]) + |> html("") + end +end diff --git a/lib/pleroma/web/akkoma_api/views/frontend_switcher.ex b/lib/pleroma/web/akkoma_api/views/frontend_switcher.ex new file mode 100644 index 000000000..1564c9e44 --- /dev/null +++ b/lib/pleroma/web/akkoma_api/views/frontend_switcher.ex @@ -0,0 +1,3 @@ +defmodule Pleroma.Web.AkkomaAPI.FrontendSwitcherView do + use Pleroma.Web, :view +end diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex index 62283353e..91dfc77c3 100644 --- a/lib/pleroma/web/plugs/frontend_static.ex +++ b/lib/pleroma/web/plugs/frontend_static.ex @@ -50,6 +50,7 @@ def init(opts) do end def call(conn, opts) do + IO.inspect("OPTS: #{inspect(opts)}") with false <- api_route?(conn.path_info), false <- invalid_path?(conn.path_info), true <- enabled?(opts[:if]), @@ -71,16 +72,19 @@ def preferred_frontend(conn) do Map.get(cookies, @frontend_cookie_name) end - def preferred_or_fallback(conn, fallback) do + # Only override primary frontend + def preferred_or_fallback(conn, :primary) do case preferred_frontend(conn) do nil -> - fallback + :primary frontend -> frontend end end + def preferred_or_fallback(conn, fallback), do: fallback + defp enabled?(if_opt) when is_function(if_opt), do: if_opt.() defp enabled?(true), do: true defp enabled?(_), do: false diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 3db8ddab7..d02ae3460 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -466,6 +466,13 @@ defmodule Pleroma.Web.Router do put("/statuses/:id/emoji_reactions/:emoji", EmojiReactionController, :create) end + scope "/akkoma/", Pleroma.Web.AkkomaAPI do + pipe_through(:browser) + + get("/frontend", FrontendSwitcherController, :switch) + post("/frontend", FrontendSwitcherController, :do_switch) + end + scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do pipe_through(:api) diff --git a/lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex b/lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex new file mode 100644 index 000000000..0692ddfb8 --- /dev/null +++ b/lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex @@ -0,0 +1,7 @@ +

Switch Frontend

+ +<%= form_for @conn, Routes.frontend_switcher_path(@conn, :do_switch), fn f -> %> + <%= select(f, :frontend, @choices) %> + + <%= submit do: "submit" %> +<% end %> From 1b2c24a19e86434f748a4f3cba6fb688253efb15 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 15:20:55 +0100 Subject: [PATCH 05/15] fix tests --- lib/pleroma/web/plugs/frontend_static.ex | 4 +--- test/pleroma/web/plugs/frontend_static_plug_test.exs | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex index 91dfc77c3..1d6861456 100644 --- a/lib/pleroma/web/plugs/frontend_static.ex +++ b/lib/pleroma/web/plugs/frontend_static.ex @@ -50,7 +50,6 @@ def init(opts) do end def call(conn, opts) do - IO.inspect("OPTS: #{inspect(opts)}") with false <- api_route?(conn.path_info), false <- invalid_path?(conn.path_info), true <- enabled?(opts[:if]), @@ -83,7 +82,7 @@ def preferred_or_fallback(conn, :primary) do end end - def preferred_or_fallback(conn, fallback), do: fallback + def preferred_or_fallback(_conn, fallback), do: fallback defp enabled?(if_opt) when is_function(if_opt), do: if_opt.() defp enabled?(true), do: true @@ -106,7 +105,6 @@ defp api_route?([h | t]) do defp call_static(conn, opts, from) do opts = Map.put(opts, :from, from) - IO.inspect(opts, label: "opts") Plug.Static.call(conn, opts) end end diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index 66e6ba4ca..815e888ee 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -83,6 +83,7 @@ test "api routes are detected correctly" do "main", "ostatus_subscribe", "oauth", + "akkoma", "objects", "activities", "notice", From a079ec3a3cdfd42d2cbd51c7698c2c87828e5778 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 16:36:40 +0100 Subject: [PATCH 06/15] in dev, allow dev FE --- lib/pleroma/web/plugs/http_security_plug.ex | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index b1f1ada94..570aeefff 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -8,6 +8,8 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do require Logger + @mix_env Mix.env() + def init(opts), do: opts def call(conn, _options) do @@ -114,7 +116,12 @@ defp csp_string(conn) do style_src = "style-src 'self' '#{nonce_tag}'" font_src = "font-src 'self'" - script_src = "script-src 'self' '#{nonce_tag}'" + script_src = "script-src 'self' '#{nonce_tag}' " + script_src = if @mix_env == :dev do + "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + else + script_src + end report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"] insecure = if scheme == "https", do: "upgrade-insecure-requests" From 9e8e7cc13ebd2ec2ecbf90ae10c3532f77af7a81 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 16:55:48 +0100 Subject: [PATCH 07/15] Add note telling people to refresh --- .../web/templates/akkoma_api/frontend_switcher/switch.html.eex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex b/lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex index 0692ddfb8..a0b0a2361 100644 --- a/lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex +++ b/lib/pleroma/web/templates/akkoma_api/frontend_switcher/switch.html.eex @@ -1,7 +1,10 @@

Switch Frontend

+

After you submit, you will need to refresh manually to get your new frontend!

+ <%= form_for @conn, Routes.frontend_switcher_path(@conn, :do_switch), fn f -> %> <%= select(f, :frontend, @choices) %> <%= submit do: "submit" %> <% end %> + From ba59fdcd54986b0ebc31a577cb1c767e55a43e7f Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 16:56:51 +0100 Subject: [PATCH 08/15] add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf6253af..a277ac08c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Nodeinfo keys for unauthenticated timeline visibility - Option to disable federated timeline - Option to make the bubble timeline publicly accessible +- Ability to swap between installed standard frontends + - *mastodon frontends are still not counted as standard frontends due to the complexity in serving them correctly*. ## 2023.03 From f12d3cce39e6bdfbe4aa8364ba1051a6b72155fc Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 17:42:40 +0100 Subject: [PATCH 09/15] ensure only pickable frontends can be returned --- lib/pleroma/web/plugs/frontend_static.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex index 1d6861456..41b8ba46b 100644 --- a/lib/pleroma/web/plugs/frontend_static.ex +++ b/lib/pleroma/web/plugs/frontend_static.ex @@ -78,7 +78,11 @@ def preferred_or_fallback(conn, :primary) do :primary frontend -> - frontend + if Enum.member?(Pleroma.Config.get([:frontends, :pickable], []), frontend) do + frontend + else + :primary + end end end From d2b0d864718c676af9909ee45fac60d5f232bf6a Mon Sep 17 00:00:00 2001 From: Atsuko Karagi Date: Tue, 4 Apr 2023 17:41:13 +0200 Subject: [PATCH 10/15] HTTP signatures respect allowlist federation --- lib/pleroma/web/activity_pub/publisher.ex | 19 +++++++-- ...mapped_signature_to_identity_plug_test.exs | 41 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index b187d3a48..3071c1b77 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -108,15 +108,28 @@ defp blocked_instances do Config.get([:mrf_simple, :reject], []) end + defp allowed_instances do + Config.get([:mrf_simple, :accept]) + end + def should_federate?(url) do %{host: host} = URI.parse(url) - quarantined_instances = - blocked_instances() + with allowed <- allowed_instances(), + false <- Enum.empty?(allowed) do + allowed |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples() |> Pleroma.Web.ActivityPub.MRF.subdomains_regex() + |> Pleroma.Web.ActivityPub.MRF.subdomain_match?(host) + else + _ -> + quarantined_instances = + blocked_instances() + |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples() + |> Pleroma.Web.ActivityPub.MRF.subdomains_regex() - !Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host) + not Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host) + end end @spec recipients(User.t(), Activity.t()) :: list(User.t()) | [] diff --git a/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs index 21c574ba3..c42b82810 100644 --- a/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs +++ b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs @@ -69,6 +69,47 @@ test "it considers a mapped identity to be invalid when the associated instance assert %{valid_signature: false} == conn.assigns end + test "allowlist federation: it considers a mapped identity to be valid when the associated instance is allowed" do + clear_config([:activitypub, :authorized_fetch_mode], true) + + clear_config([:mrf_simple, :accept], [ + {"mastodon.example.org", "anime is allowed"} + ]) + + on_exit(fn -> + Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false) + Pleroma.Config.put([:mrf_simple, :accept], []) + end) + + conn = + build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) + |> set_signature("http://mastodon.example.org/users/admin") + |> MappedSignatureToIdentityPlug.call(%{}) + + assert conn.assigns[:valid_signature] + refute is_nil(conn.assigns.user) + end + + test "allowlist federation: it considers a mapped identity to be invalid when the associated instance is not allowed" do + clear_config([:activitypub, :authorized_fetch_mode], true) + + clear_config([:mrf_simple, :accept], [ + {"misskey.example.org", "anime is allowed"} + ]) + + on_exit(fn -> + Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false) + Pleroma.Config.put([:mrf_simple, :accept], []) + end) + + conn = + build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) + |> set_signature("http://mastodon.example.org/users/admin") + |> MappedSignatureToIdentityPlug.call(%{}) + + assert %{valid_signature: false} == conn.assigns + end + @tag skip: "known breakage; the testsuite presently depends on it" test "it considers a mapped identity to be invalid when the identity cannot be found" do conn = From 1fa3c0b485fd60df8ed78531435dbf58db375b03 Mon Sep 17 00:00:00 2001 From: Atsuko Karagi Date: Sat, 8 Apr 2023 18:06:58 +0200 Subject: [PATCH 11/15] Remove support for outdated Create format --- .../activity_pub/activity_pub_controller.ex | 21 -------------- .../activity_pub_controller_test.exs | 29 ------------------- 2 files changed, 50 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index c07f91b2e..5d7297c23 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -293,33 +293,12 @@ def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do |> json("Invalid HTTP Signature") end - # POST /relay/inbox -or- POST /internal/fetch/inbox - def inbox(conn, %{"type" => "Create"} = params) do - if FederatingPlug.federating?() do - post_inbox_relayed_create(conn, params) - else - conn - |> put_status(:bad_request) - |> json("Not federating") - end - end - def inbox(conn, _params) do conn |> put_status(:bad_request) |> json("error, missing HTTP Signature") end - defp post_inbox_relayed_create(conn, params) do - Logger.debug( - "Signature missing or not from author, relayed Create message, fetching object from source" - ) - - Fetcher.fetch_object_from_id(params["object"]["id"]) - - json(conn, "ok") - end - defp represent_service_actor(%User{} = user, conn) do conn |> put_resp_content_type("application/activity+json") diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 2008ebf04..0d4a7ec2e 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -662,35 +662,6 @@ test "accept follow activity", %{conn: conn} do assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]} end - @tag capture_log: true - test "without valid signature, " <> - "it only accepts Create activities and requires enabled federation", - %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!() - non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!() - - conn = put_req_header(conn, "content-type", "application/activity+json") - - clear_config([:instance, :federating], false) - - conn - |> post("/inbox", data) - |> json_response(403) - - conn - |> post("/inbox", non_create_data) - |> json_response(403) - - clear_config([:instance, :federating], true) - - ret_conn = post(conn, "/inbox", data) - assert "ok" == json_response(ret_conn, 200) - - conn - |> post("/inbox", non_create_data) - |> json_response(400) - end - test "accepts Add/Remove activities", %{conn: conn} do object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" From 522221f7fb76859b23840bb0c9de0018dae41cbf Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 17:56:34 +0100 Subject: [PATCH 12/15] Mix format --- config/description.exs | 8 ++++---- lib/mix/tasks/pleroma/diagnostics.ex | 4 ++-- .../web/activity_pub/activity_pub_controller.ex | 1 - lib/pleroma/web/nodeinfo/nodeinfo_controller.ex | 5 ----- lib/pleroma/web/plugs/http_security_plug.ex | 12 +++++++----- .../controllers/timeline_controller_test.exs | 7 +++---- 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/config/description.exs b/config/description.exs index d329f8afa..bd20cb239 100644 --- a/config/description.exs +++ b/config/description.exs @@ -3161,10 +3161,10 @@ children: frontend_options }, %{ - key: :pickable, - type: {:list, :string}, - description: - "A list containing all frontends users can pick as their preference, format is :name/:ref, e.g pleroma-fe/stable." + key: :pickable, + type: {:list, :string}, + description: + "A list containing all frontends users can pick as their preference, format is :name/:ref, e.g pleroma-fe/stable." } ] }, diff --git a/lib/mix/tasks/pleroma/diagnostics.ex b/lib/mix/tasks/pleroma/diagnostics.ex index 3b6063723..87be38b78 100644 --- a/lib/mix/tasks/pleroma/diagnostics.ex +++ b/lib/mix/tasks/pleroma/diagnostics.ex @@ -120,8 +120,8 @@ def run(["known_network", nickname]) do params ) |> limit(20) - + Ecto.Adapters.SQL.explain(Repo, :all, query, analyze: true, timeout: :infinity) - |> IO.puts() + |> IO.puts() end end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 5d7297c23..4e6842d85 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do alias Pleroma.Activity alias Pleroma.Delivery alias Pleroma.Object - alias Pleroma.Object.Fetcher alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.InternalFetchActor diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 4c5a36895..ea2d86f92 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -5,11 +5,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do use Pleroma.Web, :controller - alias Pleroma.Config - alias Pleroma.Stats - alias Pleroma.User - alias Pleroma.Web.Federator.Publisher - alias Pleroma.Web.MastodonAPI.InstanceView alias Pleroma.Web.Endpoint alias Pleroma.Web.Nodeinfo.Nodeinfo diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index 570aeefff..d7cff7343 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -117,11 +117,13 @@ defp csp_string(conn) do font_src = "font-src 'self'" script_src = "script-src 'self' '#{nonce_tag}' " - script_src = if @mix_env == :dev do - "script-src 'self' 'unsafe-eval' 'unsafe-inline'" - else - script_src - end + + script_src = + if @mix_env == :dev do + "script-src 'self' 'unsafe-eval' 'unsafe-inline'" + else + script_src + end report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"] insecure = if scheme == "https", do: "upgrade-insecure-requests" diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index fcc7a204e..eed12234f 100644 --- a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -423,10 +423,9 @@ test "should return 404 if disabled" do test "should not return 404 if local is specified" do clear_config([:instance, :federated_timeline_available], false) - result = - build_conn() - |> get("/api/v1/timelines/public?local=true") - |> json_response_and_validate_schema(200) + build_conn() + |> get("/api/v1/timelines/public?local=true") + |> json_response_and_validate_schema(200) end end From 963d29ad8cd2c32fe408d608bd644e1db3ebbda1 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 18:00:59 +0100 Subject: [PATCH 13/15] 2023.04 Release --- CHANGELOG.md | 7 ++++++- mix.exs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f481aad5..9ef9d4e97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## Unreleased +## 2023.04 ## Added - Nodeinfo keys for unauthenticated timeline visibility @@ -13,6 +13,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Ability to swap between installed standard frontends - *mastodon frontends are still not counted as standard frontends due to the complexity in serving them correctly*. +### Upgrade Notes +- Elixir 1.14 is now required. If your distribution does not package this, you can + use [asdf](https://asdf-vm.com/). At time of writing, elixir 1.14.3 / erlang 25.3 + is confirmed to work. + ## 2023.03 ## Fixed diff --git a/mix.exs b/mix.exs index aa1dde667..6544529bd 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("3.7.1"), + version: version("3.7.2"), elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix] ++ Mix.compilers(), From d6bed599c8ca3f77a3936ff68cbaf036a5ae59b6 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Fri, 14 Apr 2023 18:09:59 +0100 Subject: [PATCH 14/15] correct version bump --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 6544529bd..ebcca9660 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("3.7.2"), + version: version("3.8.0"), elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix] ++ Mix.compilers(), From b86b3a9e298b400ee2e2805b987cc14a24a44ea7 Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Tue, 25 Apr 2023 13:30:20 +0100 Subject: [PATCH 15/15] Support public key URIs that incomprehensibly have GET args Fixes #528 --- CHANGELOG.md | 5 +++++ lib/pleroma/signature.ex | 1 + test/pleroma/signature_test.exs | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ef9d4e97..bb5b8fa04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## Unreleased + +## Fixed +- Support for `streams` public key URIs + ## 2023.04 ## Added diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 1c59be9c7..b229e6296 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -17,6 +17,7 @@ def key_id_to_actor_id(key_id) do key_id |> URI.parse() |> Map.put(:fragment, nil) + |> Map.put(:query, nil) |> remove_suffix(@known_suffixes) maybe_ap_id = URI.to_string(uri) diff --git a/test/pleroma/signature_test.exs b/test/pleroma/signature_test.exs index 59674bbc0..21e6ed161 100644 --- a/test/pleroma/signature_test.exs +++ b/test/pleroma/signature_test.exs @@ -114,6 +114,11 @@ test "it deduces the actor id for gotoSocial" do {:ok, "https://example.com/users/1234"} end + test "it deduces the actor ID for streams" do + assert Signature.key_id_to_actor_id("https://example.com/users/1234?operation=getkey") == + {:ok, "https://example.com/users/1234"} + end + test "it calls webfinger for 'acct:' accounts" do with_mock(Pleroma.Web.WebFinger, finger: fn _ -> {:ok, %{"ap_id" => "https://gensokyo.2hu/users/raymoo"}} end