diff --git a/lib/mv_web/live/global_settings_live.ex b/lib/mv_web/live/global_settings_live.ex
index 1a7e13b..3da4aa6 100644
--- a/lib/mv_web/live/global_settings_live.ex
+++ b/lib/mv_web/live/global_settings_live.ex
@@ -45,12 +45,21 @@ defmodule MvWeb.GlobalSettingsLive do
|> assign(:active_editing_section, nil)
|> assign(:locale, locale)
|> assign(:vereinfacht_env_configured, Mv.Config.vereinfacht_env_configured?())
+ |> assign(:vereinfacht_api_url_env_set, Mv.Config.vereinfacht_api_url_env_set?())
+ |> assign(:vereinfacht_api_key_env_set, Mv.Config.vereinfacht_api_key_env_set?())
+ |> assign(:vereinfacht_club_id_env_set, Mv.Config.vereinfacht_club_id_env_set?())
+ |> assign(:vereinfacht_api_key_set, present?(settings.vereinfacht_api_key))
|> assign(:last_vereinfacht_sync_result, nil)
|> assign_form()
{:ok, socket}
end
+ defp present?(nil), do: false
+ defp present?(""), do: false
+ defp present?(s) when is_binary(s), do: String.trim(s) != ""
+ defp present?(_), do: false
+
@impl true
def render(assigns) do
~H"""
@@ -83,9 +92,7 @@ defmodule MvWeb.GlobalSettingsLive do
<.form_section title={gettext("Vereinfacht Integration")}>
<%= if @vereinfacht_env_configured do %>
- {gettext(
- "Configured via environment variables (VEREINFACHT_API_URL, VEREINFACHT_API_KEY, VEREINFACHT_CLUB_ID). Fields below are read-only."
- )}
+ {gettext("Some values are set via environment variables. Those fields are read-only.")}
<% end %>
<.form for={@form} id="vereinfacht-form" phx-change="validate" phx-submit="save">
@@ -94,35 +101,53 @@ defmodule MvWeb.GlobalSettingsLive do
field={@form[:vereinfacht_api_url]}
type="text"
label={gettext("API URL")}
- disabled={@vereinfacht_env_configured}
+ disabled={@vereinfacht_api_url_env_set}
placeholder={
- if(@vereinfacht_env_configured,
+ if(@vereinfacht_api_url_env_set,
do: gettext("From VEREINFACHT_API_URL"),
else: "https://api.verein.visuel.dev/api/v1"
)
}
/>
- <.input
- field={@form[:vereinfacht_api_key]}
- type="password"
- label={gettext("API Key")}
- disabled={@vereinfacht_env_configured}
- placeholder={
- if(@vereinfacht_env_configured, do: gettext("From VEREINFACHT_API_KEY"), else: nil)
- }
- />
+
+
+ {gettext("API Key")}
+ <%= if @vereinfacht_api_key_set do %>
+ {gettext("(set)")}
+ <% end %>
+
+ <.input
+ field={@form[:vereinfacht_api_key]}
+ type="password"
+ label=""
+ disabled={@vereinfacht_api_key_env_set}
+ placeholder={
+ if(@vereinfacht_api_key_env_set,
+ do: gettext("From VEREINFACHT_API_KEY"),
+ else:
+ if(@vereinfacht_api_key_set,
+ do: gettext("Leave blank to keep current"),
+ else: nil
+ )
+ )
+ }
+ />
+
<.input
field={@form[:vereinfacht_club_id]}
type="text"
label={gettext("Club ID")}
- disabled={@vereinfacht_env_configured}
+ disabled={@vereinfacht_club_id_env_set}
placeholder={
- if(@vereinfacht_env_configured, do: gettext("From VEREINFACHT_CLUB_ID"), else: "2")
+ if(@vereinfacht_club_id_env_set, do: gettext("From VEREINFACHT_CLUB_ID"), else: "2")
}
/>
<.button
- :if={not @vereinfacht_env_configured}
+ :if={
+ not (@vereinfacht_api_url_env_set and @vereinfacht_api_key_env_set and
+ @vereinfacht_club_id_env_set)
+ }
phx-disable-with={gettext("Saving...")}
variant="primary"
class="mt-2"
@@ -206,15 +231,17 @@ defmodule MvWeb.GlobalSettingsLive do
@impl true
def handle_event("save", %{"setting" => setting_params}, socket) do
actor = MvWeb.LiveHelpers.current_actor(socket)
+ # Never send blank API key so we do not overwrite the stored secret (security)
+ setting_params_clean = drop_blank_vereinfacht_api_key(setting_params)
- case MvWeb.LiveHelpers.submit_form(socket.assigns.form, setting_params, actor) do
+ case MvWeb.LiveHelpers.submit_form(socket.assigns.form, setting_params_clean, actor) do
{:ok, _updated_settings} ->
- # Reload settings from database to ensure all dependent data is updated
{:ok, fresh_settings} = Membership.get_settings()
socket =
socket
|> assign(:settings, fresh_settings)
+ |> assign(:vereinfacht_api_key_set, present?(fresh_settings.vereinfacht_api_key))
|> put_flash(:info, gettext("Settings updated successfully"))
|> assign_form()
@@ -225,6 +252,16 @@ defmodule MvWeb.GlobalSettingsLive do
end
end
+ defp drop_blank_vereinfacht_api_key(params) when is_map(params) do
+ case params do
+ %{"vereinfacht_api_key" => v} when v in [nil, ""] ->
+ Map.delete(params, "vereinfacht_api_key")
+
+ _ ->
+ params
+ end
+ end
+
@impl true
def handle_info({:custom_field_saved, _custom_field, action}, socket) do
send_update(MvWeb.CustomFieldLive.IndexComponent,
@@ -305,9 +342,12 @@ defmodule MvWeb.GlobalSettingsLive do
end
defp assign_form(%{assigns: %{settings: settings}} = socket) do
+ # Never put API key into form/DOM to avoid secret leak in source or DevTools
+ settings_for_form = %{settings | vereinfacht_api_key: nil}
+
form =
AshPhoenix.Form.for_update(
- settings,
+ settings_for_form,
:update,
api: Membership,
as: "setting",