diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7fb603d..feda41320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list. - NodeInfo: `pleroma_emoji_reactions` to the `features` list. - Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses. +- Configuration: Add `:database_config_whitelist` setting to whitelist settings which can be configured from AdminFE. - New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required. - Mix task to create trusted OAuth App. - Notifications: Added `follow_request` notification type. diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 707d7fdbd..1078c4e87 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -911,6 +911,21 @@ config :auto_linker, Boolean, enables/disables in-database configuration. Read [Transfering the config to/from the database](../administration/CLI_tasks/config.md) for more information. +## :database_config_whitelist + +List of valid configuration sections which are allowed to be configured from the +database. Settings stored in the database before the whitelist is configured are +still applied, so it is suggested to only use the whitelist on instances that +have not migrated the config to the database. + +Example: +```elixir +config :pleroma, :database_config_whitelist, [ + {:pleroma, :instance}, + {:pleroma, Pleroma.Web.Metadata}, + {:auto_linker} +] +``` ### Multi-factor authentication - :two_factor_authentication * `totp` - a list containing TOTP configuration diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex index 74f8b2615..d1cf1f487 100644 --- a/lib/pleroma/docs/json.ex +++ b/lib/pleroma/docs/json.ex @@ -18,7 +18,6 @@ defmodule Pleroma.Docs.JSON do with config <- Pleroma.Config.Loader.read("config/description.exs") do config[:pleroma][:config_description] |> Pleroma.Docs.Generator.convert_to_strings() - |> Jason.encode!() end end end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 9821173d0..451dc92d6 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -37,7 +37,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do require Logger - @descriptions_json Pleroma.Docs.JSON.compile() + @descriptions Pleroma.Docs.JSON.compile() @users_page_size 50 plug( @@ -897,9 +897,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def config_descriptions(conn, _params) do - conn - |> Plug.Conn.put_resp_content_type("application/json") - |> Plug.Conn.send_resp(200, @descriptions_json) + descriptions = Enum.filter(@descriptions, &whitelisted_config?/1) + + json(conn, descriptions) end def config_show(conn, %{"only_db" => true}) do @@ -954,7 +954,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do def config_update(conn, %{"configs" => configs}) do with :ok <- configurable_from_database(conn) do {_errors, results} = - Enum.map(configs, fn + configs + |> Enum.filter(&whitelisted_config?/1) + |> Enum.map(fn %{"group" => group, "key" => key, "delete" => true} = params -> ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]}) @@ -1016,6 +1018,28 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end + defp whitelisted_config?(group, key) do + if whitelisted_configs = Config.get(:database_config_whitelist) do + Enum.any?(whitelisted_configs, fn + {whitelisted_group} -> + group == inspect(whitelisted_group) + + {whitelisted_group, whitelisted_key} -> + group == inspect(whitelisted_group) && key == inspect(whitelisted_key) + end) + else + true + end + end + + defp whitelisted_config?(%{"group" => group, "key" => key}) do + whitelisted_config?(group, key) + end + + defp whitelisted_config?(%{:group => group} = config) do + whitelisted_config?(group, config[:key]) + end + def reload_emoji(conn, _params) do Pleroma.Emoji.reload() diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index e4b0dd627..9b7120712 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2940,6 +2940,33 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value assert ":proxy_url" in db end + + test "doesn't set keys not in the whitelist", %{conn: conn} do + clear_config(:database_config_whitelist, [ + {:pleroma, :key1}, + {:pleroma, :key2}, + {:pleroma, Pleroma.Captcha.NotReal}, + {:not_real} + ]) + + post(conn, "/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":key1", value: "value1"}, + %{group: ":pleroma", key: ":key2", value: "value2"}, + %{group: ":pleroma", key: ":key3", value: "value3"}, + %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"}, + %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"}, + %{group: ":not_real", key: ":anything", value: "value6"} + ] + }) + + assert Application.get_env(:pleroma, :key1) == "value1" + assert Application.get_env(:pleroma, :key2) == "value2" + assert Application.get_env(:pleroma, :key3) == nil + assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil + assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5" + assert Application.get_env(:not_real, :anything) == "value6" + end end describe "GET /api/pleroma/admin/restart" do @@ -3571,19 +3598,54 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end - test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do - admin = insert(:user, is_admin: true) + describe "GET /api/pleroma/admin/config/descriptions" do + test "structure", %{conn: conn} do + admin = insert(:user, is_admin: true) - conn = - assign(conn, :user, admin) - |> get("/api/pleroma/admin/config/descriptions") + conn = + assign(conn, :user, admin) + |> get("/api/pleroma/admin/config/descriptions") - assert [child | _others] = json_response(conn, 200) + assert [child | _others] = json_response(conn, 200) - assert child["children"] - assert child["key"] - assert String.starts_with?(child["group"], ":") - assert child["description"] + assert child["children"] + assert child["key"] + assert String.starts_with?(child["group"], ":") + assert child["description"] + end + + test "filters by database configuration whitelist", %{conn: conn} do + clear_config(:database_config_whitelist, [ + {:pleroma, :instance}, + {:pleroma, :activitypub}, + {:pleroma, Pleroma.Upload}, + {:esshd} + ]) + + admin = insert(:user, is_admin: true) + + conn = + assign(conn, :user, admin) + |> get("/api/pleroma/admin/config/descriptions") + + children = json_response(conn, 200) + + assert length(children) == 4 + + assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3 + + instance = Enum.find(children, fn c -> c["key"] == ":instance" end) + assert instance["children"] + + activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end) + assert activitypub["children"] + + web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end) + assert web_endpoint["children"] + + esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end) + assert esshd["children"] + end end describe "/api/pleroma/admin/stats" do