Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix double slash in redirect URL redirect location #1515

Merged
merged 5 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
#
{Credo.Check.Refactor.Apply, []},
{Credo.Check.Refactor.CondStatements, []},
{Credo.Check.Refactor.CyclomaticComplexity, []},
{Credo.Check.Refactor.CyclomaticComplexity, [max_complexity: 12]},
{Credo.Check.Refactor.FunctionArity, []},
{Credo.Check.Refactor.LongQuoteBlocks, []},
{Credo.Check.Refactor.MatchInCondition, []},
Expand Down
9 changes: 7 additions & 2 deletions apps/fz_http/lib/fz_http/auth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ defmodule FzHttp.Auth do

def fetch_oidc_provider_config(provider_id) do
with {:ok, provider} <- fetch_provider(:openid_connect_providers, provider_id) do
external_url = FzHttp.Config.fetch_env!(:fz_http, :external_url)
redirect_uri = provider.redirect_uri || "#{external_url}/auth/oidc/#{provider.id}/callback/"
redirect_uri =
if provider.redirect_uri do
provider.redirect_uri
else
external_url = FzHttp.Config.fetch_env!(:fz_http, :external_url)
"#{external_url}auth/oidc/#{provider.id}/callback/"
end

{:ok,
%{
Expand Down
2 changes: 1 addition & 1 deletion apps/fz_http/lib/fz_http/config/definitions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ defmodule FzHttp.Config.Definitions do
defconfig(:external_url, :string,
changeset: fn changeset, key ->
changeset
|> FzHttp.Validator.validate_uri(key)
|> FzHttp.Validator.validate_uri(key, require_trailing_slash: true)
|> FzHttp.Validator.normalize_url(key)
end
)
Expand Down
15 changes: 14 additions & 1 deletion apps/fz_http/lib/fz_http/validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule FzHttp.Validator do

def validate_uri(changeset, field, opts \\ []) when is_atom(field) do
valid_schemes = Keyword.get(opts, :schemes, ~w[http https])
require_trailing_slash? = Keyword.get(opts, :require_trailing_slash, false)

validate_change(changeset, field, fn _current_field, value ->
case URI.new(value) do
Expand All @@ -32,6 +33,10 @@ defmodule FzHttp.Validator do
uri.scheme not in valid_schemes ->
[{field, "only #{Enum.join(valid_schemes, ", ")} schemes are supported"}]

require_trailing_slash? and not is_nil(uri.path) and
not String.ends_with?(uri.path, "/") ->
[{field, "does not end with a trailing slash"}]

true ->
[]
end
Expand All @@ -48,7 +53,7 @@ defmodule FzHttp.Validator do
uri = URI.parse(value)
scheme = uri.scheme || "https"
port = URI.default_port(scheme)
path = uri.path || "/"
path = maybe_add_trailing_slash(uri.path || "/")
uri = %{uri | scheme: scheme, port: port, path: path}
uri_string = URI.to_string(uri)
put_change(changeset, field, uri_string)
Expand All @@ -57,6 +62,14 @@ defmodule FzHttp.Validator do
end
end

defp maybe_add_trailing_slash(value) do
if String.ends_with?(value, "/") do
value
else
value <> "/"
end
end

def validate_no_duplicates(changeset, field) when is_atom(field) do
validate_change(changeset, field, fn _current_field, list when is_list(list) ->
list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,7 @@ defmodule FzHttpWeb.SettingLive.OIDCFormComponent do
<div class="control">
<%= text_input(f, :redirect_uri,
placeholder:
Path.join(
@external_url,
"auth/oidc/#{input_value(f, :id) || "{CONFIG_ID}"}/callback/"
),
"#{@external_url}auth/oidc/#{input_value(f, :id) || "{CONFIG_ID}"}/callback/",
class: "input #{input_error_class(f, :redirect_uri)}"
) %>
</div>
Expand All @@ -147,10 +144,7 @@ defmodule FzHttpWeb.SettingLive.OIDCFormComponent do
Optionally override the Redirect URI. Must match the redirect URI set in your IdP.
In most cases you shouldn't change this. By default
<code>
<%= Path.join(
@external_url,
"auth/oidc/#{input_value(f, :id) || "{CONFIG_ID}"}/callback/"
) %>
<%= "#{@external_url}auth/oidc/#{input_value(f, :id) || "{CONFIG_ID}"}/callback/" %>
</code>
is used.
</p>
Expand Down
2 changes: 1 addition & 1 deletion apps/fz_http/test/fz_http/auth_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule FzHttp.AuthTest do
client_id: attrs["client_id"],
client_secret: attrs["client_secret"],
discovery_document_uri: attrs["discovery_document_uri"],
redirect_uri: "http://foo.bar.com//auth/oidc/google/callback/",
redirect_uri: "http://foo.bar.com/auth/oidc/google/callback/",
response_type: attrs["response_type"],
scope: attrs["scope"]
}}
Expand Down
28 changes: 28 additions & 0 deletions apps/fz_http/test/fz_http/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,34 @@ defmodule FzHttp.ConfigTest do
eg: `https://firezone.mycorp.com/vpn`.


You can find more information on configuration here: https://www.firezone.dev/docs/reference/env-vars/#environment-variable-listing
"""

assert_raise RuntimeError, message, fn ->
fetch_source_and_configs!([:external_url])
end
end

test "raises an error when value is invalid" do
put_system_env_override(:external_url, "https://example.com/vpn")

message = """
Invalid configuration for 'external_url' retrieved from environment variable EXTERNAL_URL.

Errors:

- `"https://example.com/vpn"`: does not end with a trailing slash

## Documentation

The external URL the web UI will be accessible at.

Must be a valid and public FQDN for ACME SSL issuance to function.

You can add a path suffix if you want to serve firezone from a non-root path,
eg: `https://firezone.mycorp.com/vpn`.


You can find more information on configuration here: https://www.firezone.dev/docs/reference/env-vars/#environment-variable-listing
"""

Expand Down