Merge pull request 'Fix preferred frontends' (#7911) from phnt/pleroma:fix-preferred-frontend into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7911
This commit is contained in:
commit
0a076443ad
8 changed files with 128 additions and 74 deletions
1
changelog.d/fix-preferred-frontend.fix
Normal file
1
changelog.d/fix-preferred-frontend.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix preferred frontend not working due to InstanceStatic plug prematurely returning HTTP 404
|
||||
|
|
@ -139,6 +139,8 @@ defmodule Pleroma.Web.Endpoint do
|
|||
from: {:pleroma, "priv/static/adminfe/"}
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.Plugs.StaticNotFoundPlug)
|
||||
|
||||
# Code reloading can be explicitly enabled under the
|
||||
# :code_reloader configuration of your endpoint.
|
||||
if code_reloading? do
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.Plugs.EnsureHostMatchesPlug do
|
||||
@moduledoc "Ensures Host header matches instance"
|
||||
@behaviour Plug
|
||||
|
||||
alias Pleroma.Web.Endpoint
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.Plugs.InstanceStatic do
|
||||
require Pleroma.Constants
|
||||
import Plug.Conn, only: [put_resp_header: 3, put_status: 2, send_resp: 3, halt: 1]
|
||||
import Plug.Conn, only: [put_resp_header: 3]
|
||||
|
||||
@moduledoc """
|
||||
This is a shim to call `Plug.Static` but with runtime `from` configuration.
|
||||
|
|
@ -51,37 +51,10 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do
|
|||
|> Map.put(:from, from)
|
||||
|> Map.put(:content_types, false)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> set_content_type(conn.request_path)
|
||||
|> Plug.Static.call(opts)
|
||||
conn = set_content_type(conn, conn.request_path)
|
||||
|
||||
if conn.halted do
|
||||
conn
|
||||
else
|
||||
path = String.trim_leading(conn.request_path, "/")
|
||||
|
||||
if not File.exists?(file_path(path)) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> send_404()
|
||||
|> halt()
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp send_404(conn) do
|
||||
if String.ends_with?(String.downcase(conn.request_path), ".json") do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/json")
|
||||
|> send_resp(404, Jason.encode!(%{error: "not found"}))
|
||||
else
|
||||
conn
|
||||
|> put_resp_header("content-type", "text/plain")
|
||||
|> send_resp(404, "Not found")
|
||||
end
|
||||
# Call Plug.Static with our sanitized content-type
|
||||
Plug.Static.call(conn, opts)
|
||||
end
|
||||
|
||||
defp set_content_type(conn, "/emoji/" <> filepath) do
|
||||
|
|
|
|||
38
lib/pleroma/web/plugs/static_not_found_plug.ex
Normal file
38
lib/pleroma/web/plugs/static_not_found_plug.ex
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.StaticNotFoundPlug do
|
||||
@moduledoc """
|
||||
A small plug that enforces returning 404 on static files when they don't exist.
|
||||
"""
|
||||
@behaviour Plug
|
||||
|
||||
require Pleroma.Constants
|
||||
import Plug.Conn, only: [put_resp_header: 3, send_resp: 3, halt: 1]
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
for only <- Pleroma.Constants.static_only_files() do
|
||||
def call(%{request_path: "/" <> unquote(only) <> _} = conn, _opts) do
|
||||
send_404(conn)
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
conn
|
||||
end
|
||||
|
||||
defp send_404(conn) do
|
||||
if String.ends_with?(String.downcase(conn.request_path), ".json") do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/json")
|
||||
|> send_resp(404, Jason.encode!(%{error: "not found"}))
|
||||
else
|
||||
conn
|
||||
|> put_resp_header("content-type", "text/plain")
|
||||
|> send_resp(404, "Not found")
|
||||
end
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
|
@ -122,16 +122,27 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
|||
|
||||
clear_config([:frontends, :pickable], ["#{name}/#{ref}"])
|
||||
path = "#{@dir}/frontends/#{name}/#{ref}"
|
||||
path2 = "#{@dir}/frontends/#{name}/#{ref}/packs/html"
|
||||
|
||||
Pleroma.Backports.mkdir_p!(path)
|
||||
File.write!("#{path}/index.html", "from frontend plug")
|
||||
|
||||
Pleroma.Backports.mkdir_p!(path2)
|
||||
File.write!("#{path2}/custom.html", "from frontend plug")
|
||||
|
||||
index =
|
||||
conn
|
||||
|> put_req_cookie("preferred_frontend", "#{name}/#{ref}")
|
||||
|> get("/")
|
||||
|
||||
assert html_response(index, 200) == "from frontend plug"
|
||||
|
||||
custom =
|
||||
conn
|
||||
|> put_req_cookie("preferred_frontend", "#{name}/#{ref}")
|
||||
|> get("/packs/html/custom.html")
|
||||
|
||||
assert html_response(custom, 200) == "from frontend plug"
|
||||
end
|
||||
|
||||
test "only returns content from pickable frontends", %{conn: conn} do
|
||||
|
|
|
|||
|
|
@ -61,6 +61,28 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
|
|||
File.write!(@dir <> "/static/kaniini.html", "<h1>rabbit hugs as a service</h1>")
|
||||
index = get(build_conn(), "/static/kaniini.html")
|
||||
assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
|
||||
|
||||
File.write!(@dir <> "/static/kaniini the bnuuy.html", "<h1>rabbit hugs as a service</h1>")
|
||||
index = get(build_conn(), "/static/kaniini%20the%20bnuuy.html")
|
||||
assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
|
||||
end
|
||||
|
||||
test "overrides /static/styles files with spaces in URL" do
|
||||
Pleroma.Backports.mkdir_p!(@dir <> "/static/styles")
|
||||
File.write(@dir <> "/static/styles/Redmond DX.iss", "overridden")
|
||||
|
||||
index = get(build_conn(), "/static/styles/Redmond%20DX.iss")
|
||||
assert index.status == 200
|
||||
assert index.resp_body == File.read!(@dir <> "/static/styles/Redmond DX.iss")
|
||||
assert index.halted == true
|
||||
end
|
||||
|
||||
test "serves fallback /static/styles files with spaces in URL" do
|
||||
index = get(build_conn(), "/static/styles/Redmond%20DX.iss")
|
||||
|
||||
assert index.status == 200
|
||||
assert index.resp_body == File.read!("priv/static/static/styles/Redmond DX.iss")
|
||||
assert index.halted == true
|
||||
end
|
||||
|
||||
test "does not sanitize dangerous files in general, as there can be html and javascript files legitimately in this folder" do
|
||||
|
|
@ -137,47 +159,4 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
|
|||
# It should be preserved because "image" is in the allowed_mime_types list
|
||||
assert content_type == "image/jpeg"
|
||||
end
|
||||
|
||||
describe "404s for missing files in static-only paths" do
|
||||
test "returns 404 for non-existent static-only JSON files" do
|
||||
conn = get(build_conn(), "/static/non-existent.json")
|
||||
|
||||
assert conn.status == 404
|
||||
assert ["application/json"] = get_resp_header(conn, "content-type")
|
||||
assert Jason.decode!(conn.resp_body) == %{"error" => "not found"}
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent static-only non-JSON files" do
|
||||
conn = get(build_conn(), "/static/non-existent.txt")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent .css files" do
|
||||
conn = get(build_conn(), "/static/non-existent.css")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
# Verifies that we forced text/plain for the error body, even though the path was .css
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent files without an extension" do
|
||||
conn = get(build_conn(), "/static/non-existent")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 200 (falls through to SPA) for non-static-only paths" do
|
||||
# /some-route is NOT in static_only_files, so it should still fall through to the SPA.
|
||||
conn = get(build_conn(), "/some-route")
|
||||
|
||||
assert conn.status == 200
|
||||
assert ["text/html; charset=utf-8"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
49
test/pleroma/web/plugs/static_not_found_plug_test.exs
Normal file
49
test/pleroma/web/plugs/static_not_found_plug_test.exs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
defmodule Pleroma.Web.Plugs.StaticNotFoundPlugTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
describe "404s for missing files in static-only paths" do
|
||||
test "returns 404 for non-existent static-only JSON files" do
|
||||
conn = get(build_conn(), "/static/non-existent.json")
|
||||
|
||||
assert conn.status == 404
|
||||
assert ["application/json"] = get_resp_header(conn, "content-type")
|
||||
assert Jason.decode!(conn.resp_body) == %{"error" => "not found"}
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent static-only non-JSON files" do
|
||||
conn = get(build_conn(), "/static/non-existent.txt")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent .css files" do
|
||||
conn = get(build_conn(), "/static/non-existent.css")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
# Verifies that we forced text/plain for the error body, even though the path was .css
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent files without an extension" do
|
||||
conn = get(build_conn(), "/static/non-existent")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 200 (falls through to SPA) for non-static-only paths" do
|
||||
# /some-route is NOT in static_only_files, so it should still fall through to the SPA.
|
||||
conn = get(build_conn(), "/some-route")
|
||||
|
||||
assert conn.status == 200
|
||||
assert ["text/html; charset=utf-8"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue