feat: improve oidc only mode
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Simon 2026-03-16 17:14:54 +01:00
parent a8d9fe6121
commit 9b4f3b140c
Signed by: simon
GPG key ID: 40E7A58C4AA1EDB2
19 changed files with 330 additions and 43 deletions

View file

@ -19,6 +19,7 @@ defmodule MvWeb.GlobalSettingsLive do
## Events
- `validate` / `save` - Club settings form
- `toggle_registration_enabled` - Enable/disable direct registration (/register)
- `toggle_oidc_only` - Enable/disable OIDC-only sign-in (immediate, outside OIDC form)
- `toggle_join_form_enabled` - Enable/disable the join form
- `add_join_form_field` / `remove_join_form_field` - Manage join form fields
- `toggle_join_form_field_required` - Toggle required flag per field
@ -80,6 +81,7 @@ defmodule MvWeb.GlobalSettingsLive do
|> assign(:oidc_admin_group_name_env_set, Mv.Config.oidc_admin_group_name_env_set?())
|> assign(:oidc_groups_claim_env_set, Mv.Config.oidc_groups_claim_env_set?())
|> assign(:oidc_only_env_set, Mv.Config.oidc_only_env_set?())
|> assign(:oidc_only, Mv.Config.oidc_only?())
|> assign(:oidc_configured, Mv.Config.oidc_configured?())
|> assign(:oidc_client_secret_set, Mv.Config.oidc_client_secret_set?())
|> assign(:registration_enabled, settings.registration_enabled != false)
@ -625,11 +627,30 @@ defmodule MvWeb.GlobalSettingsLive do
class="checkbox checkbox-sm"
checked={@registration_enabled}
phx-click="toggle_registration_enabled"
disabled={@oidc_only}
aria-label={gettext("Allow direct registration (/register)")}
/>
<label for="registration-enabled-checkbox" class="cursor-pointer font-medium">
<label
for="registration-enabled-checkbox"
class={
if @oidc_only, do: "cursor-not-allowed opacity-70", else: "cursor-pointer font-medium"
}
>
{gettext("Allow direct registration (/register)")}
</label>
<%= if @oidc_only do %>
<.tooltip
content={gettext("Only OIDC sign-in is active. This option is disabled.")}
position="top"
>
<span
data-testid="oidc-only-registration-hint"
class="cursor-help text-base-content/70"
>
</span>
</.tooltip>
<% end %>
</div>
<h3 class="font-medium mb-3">{gettext("OIDC (Single Sign-On)")}</h3>
@ -638,6 +659,38 @@ defmodule MvWeb.GlobalSettingsLive do
{gettext("Some values are set via environment variables. Those fields are read-only.")}
</p>
<% end %>
<div class="flex items-center gap-3 mb-4">
<input
type="checkbox"
id="oidc-only-checkbox"
data-testid="oidc-only-checkbox"
class="checkbox checkbox-sm"
checked={@oidc_only}
phx-click="toggle_oidc_only"
disabled={@oidc_only_env_set or not @oidc_configured}
aria-label={gettext("Only OIDC sign-in (hide password login)")}
/>
<label
for="oidc-only-checkbox"
class={
if @oidc_only_env_set or not @oidc_configured,
do: "cursor-not-allowed opacity-70",
else: "cursor-pointer font-medium"
}
>
{if @oidc_only_env_set do
gettext("Only OIDC sign-in (hide password login)") <>
" (" <> gettext("From OIDC_ONLY") <> ")"
else
gettext("Only OIDC sign-in (hide password login)")
end}
</label>
</div>
<p class="label-text-alt text-base-content/70 mb-4">
{gettext(
"When enabled and OIDC is configured, the sign-in page shows only the Single Sign-On button."
)}
</p>
<.form for={@form} id="oidc-form" phx-change="validate" phx-submit="save">
<div class="grid gap-4">
<.input
@ -744,27 +797,6 @@ defmodule MvWeb.GlobalSettingsLive do
)
}
/>
<div class="form-control">
<.input
field={@form[:oidc_only]}
type="checkbox"
class="checkbox checkbox-sm"
disabled={@oidc_only_env_set or not @oidc_configured}
label={
if @oidc_only_env_set do
gettext("Only OIDC sign-in (hide password login)") <>
" (" <> gettext("From OIDC_ONLY") <> ")"
else
gettext("Only OIDC sign-in (hide password login)")
end
}
/>
<p class="label-text-alt text-base-content/70 mt-1">
{gettext(
"When enabled and OIDC is configured, the sign-in page shows only the Single Sign-On button."
)}
</p>
</div>
</div>
<.button
:if={
@ -880,6 +912,7 @@ defmodule MvWeb.GlobalSettingsLive do
|> assign(:registration_enabled, fresh_settings.registration_enabled != false)
|> assign(:vereinfacht_api_key_set, present?(fresh_settings.vereinfacht_api_key))
|> assign(:oidc_client_secret_set, Mv.Config.oidc_client_secret_set?())
|> assign(:oidc_only, Mv.Config.oidc_only?())
|> assign(:oidc_configured, Mv.Config.oidc_configured?())
|> assign(:smtp_configured, Mv.Config.smtp_configured?())
|> assign(:smtp_password_set, present?(Mv.Config.smtp_password()))
@ -916,19 +949,53 @@ defmodule MvWeb.GlobalSettingsLive do
@impl true
def handle_event("toggle_registration_enabled", _params, socket) do
settings = socket.assigns.settings
new_value = not socket.assigns.registration_enabled
if Mv.Config.oidc_only?() do
{:noreply, socket}
else
settings = socket.assigns.settings
new_value = not socket.assigns.registration_enabled
case Membership.update_settings(settings, %{registration_enabled: new_value}) do
{:ok, updated_settings} ->
{:noreply,
socket
|> assign(:settings, updated_settings)
|> assign(:registration_enabled, updated_settings.registration_enabled != false)
|> assign_form()}
case Membership.update_settings(settings, %{registration_enabled: new_value}) do
{:ok, updated_settings} ->
{:noreply,
socket
|> assign(:settings, updated_settings)
|> assign(:registration_enabled, updated_settings.registration_enabled != false)
|> assign_form()}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to update setting."))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to update setting."))}
end
end
end
@impl true
def handle_event("toggle_oidc_only", _params, socket) do
if socket.assigns.oidc_only_env_set do
{:noreply, socket}
else
settings = socket.assigns.settings
new_value = not socket.assigns.oidc_only
# When enabling OIDC-only, also disable direct registration; when disabling, only change oidc_only.
params =
if new_value,
do: %{oidc_only: true, registration_enabled: false},
else: %{oidc_only: false}
case Membership.update_settings(settings, params) do
{:ok, updated_settings} ->
{:noreply,
socket
|> assign(:settings, updated_settings)
|> assign(:oidc_only, updated_settings.oidc_only == true)
|> assign(:registration_enabled, updated_settings.registration_enabled != false)
|> assign_form()
|> put_flash(:success, gettext("Settings updated successfully"))}
{:error, _} ->
{:noreply, put_flash(socket, :error, gettext("Failed to update setting."))}
end
end
end