From e6f91b8bccd67df2396b6f2209fff014f1d2078f Mon Sep 17 00:00:00 2001 From: Yurii Cherniavskyi Date: Sun, 18 Jun 2023 13:11:54 +0300 Subject: [PATCH 1/5] Generalise SwaggerUI plug config supplementing for Phoenix app --- lib/open_api_spex/plug/swagger_ui.ex | 24 +++++++++++----- test/plug/swagger_ui_test.exs | 42 +++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/lib/open_api_spex/plug/swagger_ui.ex b/lib/open_api_spex/plug/swagger_ui.ex index 6c3ddd6c..3b8c6ee4 100644 --- a/lib/open_api_spex/plug/swagger_ui.ex +++ b/lib/open_api_spex/plug/swagger_ui.ex @@ -193,14 +193,24 @@ defmodule OpenApiSpex.Plug.SwaggerUI do end if Code.ensure_loaded?(Phoenix.Controller) do - defp supplement_config(%{oauth2_redirect_url: {:endpoint_url, path}} = config, conn) do + defp supplement_config(config, conn) do endpoint_module = Phoenix.Controller.endpoint_module(conn) - url = Path.join(endpoint_module.url(), path) - Map.put(config, :oauth2_redirect_url, url) - end - end - defp supplement_config(config, _conn) do - config + Enum.map(config, fn + {k, {:endpoint_url, path}} -> + {k, Path.join(endpoint_module.url(), endpoint_module.path(path))} + + {k, {:endpoint_path, path}} -> + {k, endpoint_module.path(path)} + + k_v -> + k_v + end) + |> Map.new() + end + else + defp supplement_config(config, _conn) do + config + end end end diff --git a/test/plug/swagger_ui_test.exs b/test/plug/swagger_ui_test.exs index e53b21d6..ffee03c9 100644 --- a/test/plug/swagger_ui_test.exs +++ b/test/plug/swagger_ui_test.exs @@ -3,14 +3,48 @@ defmodule OpenApiSpec.Plug.SwaggerUITest do alias OpenApiSpex.Plug.SwaggerUI - @opts SwaggerUI.init(path: "/ui") + setup_all do + start_supervised!(OpenApiSpexTest.Endpoint) + :ok + end test "renders csrf token" do + config = SwaggerUI.init(path: "/ui") + token = Plug.CSRFProtection.get_csrf_token() - conn = Plug.Test.conn(:get, "/ui") - conn = SwaggerUI.call(conn, @opts) - assert conn.resp_body =~ ~r[pathname.+?/ui] + conn = + Plug.Test.conn(:get, "/ui") + |> Plug.Conn.put_private(:phoenix_endpoint, OpenApiSpexTest.Endpoint) + + conn = SwaggerUI.call(conn, config) + assert conn.resp_body =~ ~r[pathname.+?"/ui"] assert String.contains?(conn.resp_body, token) end + + Application.put_env(:open_api_spex_test, OpenApiSpexTest.Endpoint, + url: [scheme: "https", host: "some-host.com", port: 1234, path: "/some-prefix"] + ) + + test "generate actual path dependent to endpoint" do + config = SwaggerUI.init(path: {:endpoint_path, "/ui"}) + + conn = + Plug.Test.conn(:get, "/ui") + |> Plug.Conn.put_private(:phoenix_endpoint, OpenApiSpexTest.Endpoint) + + conn = SwaggerUI.call(conn, config) + assert conn.resp_body =~ ~r[pathname.+?"/some-prefix/ui"] + end + + test "generate actual url dependent to endpoint" do + config = SwaggerUI.init(path: {:endpoint_url, "/ui"}) + + conn = + Plug.Test.conn(:get, "/ui") + |> Plug.Conn.put_private(:phoenix_endpoint, OpenApiSpexTest.Endpoint) + + conn = SwaggerUI.call(conn, config) + assert conn.resp_body =~ ~r[pathname.+?"https://some-host.com:1234/some-prefix/ui"] + end end From 21b65b16449601403062d1884613be717b038412 Mon Sep 17 00:00:00 2001 From: Yurii Cherniavskyi Date: Sun, 18 Jun 2023 15:07:31 +0300 Subject: [PATCH 2/5] Configure and start Endpoint for each individual test --- test/plug/swagger_ui_test.exs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/test/plug/swagger_ui_test.exs b/test/plug/swagger_ui_test.exs index ffee03c9..7aa766ea 100644 --- a/test/plug/swagger_ui_test.exs +++ b/test/plug/swagger_ui_test.exs @@ -2,47 +2,51 @@ defmodule OpenApiSpec.Plug.SwaggerUITest do use ExUnit.Case alias OpenApiSpex.Plug.SwaggerUI - - setup_all do - start_supervised!(OpenApiSpexTest.Endpoint) - :ok - end + alias OpenApiSpexTest.Endpoint test "renders csrf token" do + start_supervised!(Endpoint) + config = SwaggerUI.init(path: "/ui") token = Plug.CSRFProtection.get_csrf_token() conn = Plug.Test.conn(:get, "/ui") - |> Plug.Conn.put_private(:phoenix_endpoint, OpenApiSpexTest.Endpoint) + |> Plug.Conn.put_private(:phoenix_endpoint, Endpoint) conn = SwaggerUI.call(conn, config) assert conn.resp_body =~ ~r[pathname.+?"/ui"] assert String.contains?(conn.resp_body, token) end - Application.put_env(:open_api_spex_test, OpenApiSpexTest.Endpoint, - url: [scheme: "https", host: "some-host.com", port: 1234, path: "/some-prefix"] - ) - test "generate actual path dependent to endpoint" do + Application.put_env(:open_api_spex_test, Endpoint, url: [path: "/some-prefix"]) + + start_supervised!(Endpoint) + config = SwaggerUI.init(path: {:endpoint_path, "/ui"}) conn = Plug.Test.conn(:get, "/ui") - |> Plug.Conn.put_private(:phoenix_endpoint, OpenApiSpexTest.Endpoint) + |> Plug.Conn.put_private(:phoenix_endpoint, Endpoint) conn = SwaggerUI.call(conn, config) assert conn.resp_body =~ ~r[pathname.+?"/some-prefix/ui"] end test "generate actual url dependent to endpoint" do + Application.put_env(:open_api_spex_test, Endpoint, + url: [scheme: "https", host: "some-host.com", port: 1234, path: "/some-prefix"] + ) + + start_supervised!(Endpoint) + config = SwaggerUI.init(path: {:endpoint_url, "/ui"}) conn = Plug.Test.conn(:get, "/ui") - |> Plug.Conn.put_private(:phoenix_endpoint, OpenApiSpexTest.Endpoint) + |> Plug.Conn.put_private(:phoenix_endpoint, Endpoint) conn = SwaggerUI.call(conn, config) assert conn.resp_body =~ ~r[pathname.+?"https://some-host.com:1234/some-prefix/ui"] From 569825dbfc3ac6e8986e3a950f0721c09f4f0513 Mon Sep 17 00:00:00 2001 From: Yurii Cherniavskyi Date: Sun, 18 Jun 2023 15:09:39 +0300 Subject: [PATCH 3/5] Use dedicated function to launch Endpoint under the test supervisor --- test/server_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/server_test.exs b/test/server_test.exs index 0a984f0f..f026be43 100644 --- a/test/server_test.exs +++ b/test/server_test.exs @@ -27,6 +27,6 @@ defmodule OpenApiSpex.ServerTest do ] ) - Endpoint.start_link() + start_supervised!(Endpoint) end end From 598cbdd1b05b860fa63762b3185bfa1cf3bc2c87 Mon Sep 17 00:00:00 2001 From: Yurii Cherniavskyi Date: Sun, 18 Jun 2023 20:09:25 +0300 Subject: [PATCH 4/5] Describe special option values in Swagger UI plug --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2bcede5b..57d506b9 100644 --- a/README.md +++ b/README.md @@ -338,6 +338,8 @@ scope "/api" do end ``` +Additionally, you can set plug option values as a tuple. In this tuple, the first element should be either `:endpoint_url` or `:endpoint_path` and the second element should be the specific path. This tuple will be converted to the respective URL or path of the Endpoint. This feature is particularly useful in cases where your Phoenix app's Endpoint URL is configured with a non-empty path. It allows you to obtain the full path to the local spec or, as an example, to retrieve the URL for the `oauth2RedirectUrl` parameter, which can then be passed to the [Swagger UI configuration](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/). + ## Importing an existing schema file > :warning: This functionality currently converts Strings into Atoms, which makes it potentially [vulnerable to DoS attacks](https://til.hashrocket.com/posts/gkwwfy9xvw-converting-strings-to-atoms-safely). We recommend that you load Open API Schemas from _known files_ during application startup and _not dynamically from external sources at runtime_. From 6271d6e963a7411cfdef6b5eb7bf243c75831e6b Mon Sep 17 00:00:00 2001 From: Yurii Cherniavskyi Date: Wed, 6 Sep 2023 13:21:54 +0300 Subject: [PATCH 5/5] Optimize config map transform --- lib/open_api_spex/plug/swagger_ui.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/open_api_spex/plug/swagger_ui.ex b/lib/open_api_spex/plug/swagger_ui.ex index 3b8c6ee4..b68b0239 100644 --- a/lib/open_api_spex/plug/swagger_ui.ex +++ b/lib/open_api_spex/plug/swagger_ui.ex @@ -196,7 +196,7 @@ defmodule OpenApiSpex.Plug.SwaggerUI do defp supplement_config(config, conn) do endpoint_module = Phoenix.Controller.endpoint_module(conn) - Enum.map(config, fn + Map.new(config, fn {k, {:endpoint_url, path}} -> {k, Path.join(endpoint_module.url(), endpoint_module.path(path))} @@ -206,7 +206,6 @@ defmodule OpenApiSpex.Plug.SwaggerUI do k_v -> k_v end) - |> Map.new() end else defp supplement_config(config, _conn) do