feat: allow disabling registration
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
eb18209669
commit
09e4b64663
14 changed files with 344 additions and 5 deletions
44
docs/settings-authentication-mockup.txt
Normal file
44
docs/settings-authentication-mockup.txt
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Settings page – Authentication section (ASCII mockup)
|
||||||
|
|
||||||
|
Structure after renaming "OIDC" to "Authentication" and adding the registration toggle.
|
||||||
|
Subsections use their own headings (h3) inside the main "Authentication" form_section.
|
||||||
|
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
| Settings |
|
||||||
|
| Manage global settings for the association. |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
+-- Club Settings -------------------------------------------------+
|
||||||
|
| Association Name: [________________] [Save Name] |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
+-- Join Form -----------------------------------------------------+
|
||||||
|
| ... (unchanged) |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
+-- SMTP / E-Mail -------------------------------------------------+
|
||||||
|
| ... |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
+-- Accounting-Software (Vereinfacht) Integration -----------------+
|
||||||
|
| ... |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
||||||
|
+-- Authentication ------------------------------------------------+ <-- main section (renamed from "OIDC (Single Sign-On)")
|
||||||
|
| |
|
||||||
|
| Direct registration | <-- subsection heading (h3)
|
||||||
|
| [x] Allow direct registration (/register) |
|
||||||
|
| If disabled, users cannot sign up via /register; sign-in |
|
||||||
|
| and the join form remain available. |
|
||||||
|
| |
|
||||||
|
| OIDC (Single Sign-On) | <-- subsection heading (h3)
|
||||||
|
| (Some values are set via environment variables...) |
|
||||||
|
| Client ID: [________________] |
|
||||||
|
| Base URL: [________________] |
|
||||||
|
| Redirect URI: [________________] |
|
||||||
|
| Client Secret: [________________] (set) |
|
||||||
|
| Admin group name: [________________] |
|
||||||
|
| Groups claim: [________________] |
|
||||||
|
| [ ] Only OIDC sign-in (hide password login) |
|
||||||
|
| [Save OIDC Settings] |
|
||||||
|
+------------------------------------------------------------------+
|
||||||
|
|
@ -405,6 +405,10 @@ defmodule Mv.Accounts.User do
|
||||||
where: [action_is([:register_with_password, :admin_set_password])],
|
where: [action_is([:register_with_password, :admin_set_password])],
|
||||||
message: "must have length of at least 8"
|
message: "must have length of at least 8"
|
||||||
|
|
||||||
|
# Block direct registration when disabled in global settings
|
||||||
|
validate {Mv.Accounts.User.Validations.RegistrationEnabled, []},
|
||||||
|
where: [action_is(:register_with_password)]
|
||||||
|
|
||||||
# Email uniqueness check for all actions that change the email attribute
|
# Email uniqueness check for all actions that change the email attribute
|
||||||
# Validates that user email is not already used by another (unlinked) member
|
# Validates that user email is not already used by another (unlinked) member
|
||||||
validate Mv.Accounts.User.Validations.EmailNotUsedByOtherMember
|
validate Mv.Accounts.User.Validations.EmailNotUsedByOtherMember
|
||||||
|
|
|
||||||
27
lib/accounts/user/validations/registration_enabled.ex
Normal file
27
lib/accounts/user/validations/registration_enabled.ex
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
defmodule Mv.Accounts.User.Validations.RegistrationEnabled do
|
||||||
|
@moduledoc """
|
||||||
|
Validation that blocks direct registration (register_with_password) when
|
||||||
|
registration is disabled in global settings. Used so that even direct API/form
|
||||||
|
submissions cannot register when the setting is off.
|
||||||
|
"""
|
||||||
|
use Ash.Resource.Validation
|
||||||
|
|
||||||
|
alias Mv.Membership
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def init(opts), do: {:ok, opts}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def validate(_changeset, _opts, _context) do
|
||||||
|
case Membership.get_settings() do
|
||||||
|
{:ok, %{registration_enabled: true}} ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error,
|
||||||
|
field: :base,
|
||||||
|
message:
|
||||||
|
"Registration is disabled. Please use the join form or contact an administrator."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -15,6 +15,7 @@ defmodule Mv.Membership.Setting do
|
||||||
(e.g., `%{"first_name" => true, "last_name" => true}`). Email is always required; other fields default to optional.
|
(e.g., `%{"first_name" => true, "last_name" => true}`). Email is always required; other fields default to optional.
|
||||||
- `include_joining_cycle` - Whether to include the joining cycle in membership fee generation (default: true)
|
- `include_joining_cycle` - Whether to include the joining cycle in membership fee generation (default: true)
|
||||||
- `default_membership_fee_type_id` - Default membership fee type for new members (optional)
|
- `default_membership_fee_type_id` - Default membership fee type for new members (optional)
|
||||||
|
- `registration_enabled` - Whether direct registration via /register is allowed (default: true)
|
||||||
- `join_form_enabled` - Whether the public /join page is active (default: false)
|
- `join_form_enabled` - Whether the public /join page is active (default: false)
|
||||||
- `join_form_field_ids` - Ordered list of field IDs shown on the join form. Each entry is
|
- `join_form_field_ids` - Ordered list of field IDs shown on the join form. Each entry is
|
||||||
either a member field name string (e.g. "email") or a custom field UUID. Email is always
|
either a member field name string (e.g. "email") or a custom field UUID. Email is always
|
||||||
|
|
@ -129,6 +130,7 @@ defmodule Mv.Membership.Setting do
|
||||||
:smtp_ssl,
|
:smtp_ssl,
|
||||||
:smtp_from_name,
|
:smtp_from_name,
|
||||||
:smtp_from_email,
|
:smtp_from_email,
|
||||||
|
:registration_enabled,
|
||||||
:join_form_enabled,
|
:join_form_enabled,
|
||||||
:join_form_field_ids,
|
:join_form_field_ids,
|
||||||
:join_form_field_required
|
:join_form_field_required
|
||||||
|
|
@ -165,6 +167,7 @@ defmodule Mv.Membership.Setting do
|
||||||
:smtp_ssl,
|
:smtp_ssl,
|
||||||
:smtp_from_name,
|
:smtp_from_name,
|
||||||
:smtp_from_email,
|
:smtp_from_email,
|
||||||
|
:registration_enabled,
|
||||||
:join_form_enabled,
|
:join_form_enabled,
|
||||||
:join_form_field_ids,
|
:join_form_field_ids,
|
||||||
:join_form_field_required
|
:join_form_field_required
|
||||||
|
|
@ -514,6 +517,15 @@ defmodule Mv.Membership.Setting do
|
||||||
description "Email address for the transactional email sender. Must be owned by the SMTP user. Overrides MAIL_FROM_EMAIL env."
|
description "Email address for the transactional email sender. Must be owned by the SMTP user. Overrides MAIL_FROM_EMAIL env."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Authentication: direct registration toggle
|
||||||
|
attribute :registration_enabled, :boolean do
|
||||||
|
allow_nil? false
|
||||||
|
default true
|
||||||
|
public? true
|
||||||
|
|
||||||
|
description "When true, users can register via /register; when false, only sign-in and join form remain available."
|
||||||
|
end
|
||||||
|
|
||||||
# Join form (Beitrittsformular) settings
|
# Join form (Beitrittsformular) settings
|
||||||
attribute :join_form_enabled, :boolean do
|
attribute :join_form_enabled, :boolean do
|
||||||
allow_nil? false
|
allow_nil? false
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,19 @@ defmodule MvWeb.AuthOverrides do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule MvWeb.AuthOverridesRegistrationDisabled do
|
||||||
|
@moduledoc """
|
||||||
|
When direct registration is disabled in global settings, this override is
|
||||||
|
prepended in SignInLive so the Password component hides the "Need an account?"
|
||||||
|
toggle (register_toggle_text: nil disables the register link per library docs).
|
||||||
|
"""
|
||||||
|
use AshAuthentication.Phoenix.Overrides
|
||||||
|
|
||||||
|
override AshAuthentication.Phoenix.Components.Password do
|
||||||
|
set :register_toggle_text, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defmodule MvWeb.AuthOverridesDE do
|
defmodule MvWeb.AuthOverridesDE do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
German locale-specific overrides for AshAuthentication Phoenix components.
|
German locale-specific overrides for AshAuthentication Phoenix components.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ defmodule MvWeb.SignInLive do
|
||||||
|
|
||||||
alias AshAuthentication.Phoenix.Components
|
alias AshAuthentication.Phoenix.Components
|
||||||
alias Mv.Config
|
alias Mv.Config
|
||||||
alias MvWeb.{AuthOverridesDE, Layouts}
|
alias MvWeb.{AuthOverridesDE, AuthOverridesRegistrationDisabled, Layouts}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, session, socket) do
|
def mount(_params, session, socket) do
|
||||||
|
|
@ -36,7 +36,18 @@ defmodule MvWeb.SignInLive do
|
||||||
# without _gettext support (e.g. HorizontalRule) still render in German.
|
# without _gettext support (e.g. HorizontalRule) still render in German.
|
||||||
base_overrides = Map.get(session, "overrides", [AshAuthentication.Phoenix.Overrides.Default])
|
base_overrides = Map.get(session, "overrides", [AshAuthentication.Phoenix.Overrides.Default])
|
||||||
locale_overrides = if locale == "de", do: [AuthOverridesDE], else: []
|
locale_overrides = if locale == "de", do: [AuthOverridesDE], else: []
|
||||||
overrides = locale_overrides ++ base_overrides
|
|
||||||
|
registration_disabled =
|
||||||
|
if session["registration_enabled"] == false,
|
||||||
|
do: [AuthOverridesRegistrationDisabled],
|
||||||
|
else: []
|
||||||
|
|
||||||
|
# When registration is disabled: hide register link (register_path: nil) and hide
|
||||||
|
# "Need an account?" toggle (override register_toggle_text: nil so it takes precedence).
|
||||||
|
overrides = registration_disabled ++ locale_overrides ++ base_overrides
|
||||||
|
|
||||||
|
register_path =
|
||||||
|
if session["registration_enabled"] == false, do: nil, else: session["register_path"]
|
||||||
|
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|
|
@ -44,7 +55,7 @@ defmodule MvWeb.SignInLive do
|
||||||
|> assign_new(:otp_app, fn -> nil end)
|
|> assign_new(:otp_app, fn -> nil end)
|
||||||
|> assign(:path, session["path"] || "/")
|
|> assign(:path, session["path"] || "/")
|
||||||
|> assign(:reset_path, session["reset_path"])
|
|> assign(:reset_path, session["reset_path"])
|
||||||
|> assign(:register_path, session["register_path"])
|
|> assign(:register_path, register_path)
|
||||||
|> assign(:current_tenant, session["tenant"])
|
|> assign(:current_tenant, session["tenant"])
|
||||||
|> assign(:resources, session["resources"])
|
|> assign(:resources, session["resources"])
|
||||||
|> assign(:context, session["context"] || %{})
|
|> assign(:context, session["context"] || %{})
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@ defmodule MvWeb.GlobalSettingsLive do
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
- `club_name` - The name of the association/club (required)
|
- `club_name` - The name of the association/club (required)
|
||||||
|
- `registration_enabled` - Whether direct registration via /register is allowed
|
||||||
- `join_form_enabled` - Whether the public /join page is active
|
- `join_form_enabled` - Whether the public /join page is active
|
||||||
- `join_form_field_ids` - Ordered list of field IDs shown on the join form
|
- `join_form_field_ids` - Ordered list of field IDs shown on the join form
|
||||||
- `join_form_field_required` - Map of field ID => required boolean
|
- `join_form_field_required` - Map of field ID => required boolean
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
- `validate` / `save` - Club settings form
|
- `validate` / `save` - Club settings form
|
||||||
|
- `toggle_registration_enabled` - Enable/disable direct registration (/register)
|
||||||
- `toggle_join_form_enabled` - Enable/disable the join form
|
- `toggle_join_form_enabled` - Enable/disable the join form
|
||||||
- `add_join_form_field` / `remove_join_form_field` - Manage join form fields
|
- `add_join_form_field` / `remove_join_form_field` - Manage join form fields
|
||||||
- `toggle_join_form_field_required` - Toggle required flag per field
|
- `toggle_join_form_field_required` - Toggle required flag per field
|
||||||
|
|
@ -80,6 +82,7 @@ defmodule MvWeb.GlobalSettingsLive do
|
||||||
|> assign(:oidc_only_env_set, Mv.Config.oidc_only_env_set?())
|
|> assign(:oidc_only_env_set, Mv.Config.oidc_only_env_set?())
|
||||||
|> assign(:oidc_configured, Mv.Config.oidc_configured?())
|
|> assign(:oidc_configured, Mv.Config.oidc_configured?())
|
||||||
|> assign(:oidc_client_secret_set, Mv.Config.oidc_client_secret_set?())
|
|> assign(:oidc_client_secret_set, Mv.Config.oidc_client_secret_set?())
|
||||||
|
|> assign(:registration_enabled, settings.registration_enabled != false)
|
||||||
|> assign(:smtp_env_configured, Mv.Config.smtp_env_configured?())
|
|> assign(:smtp_env_configured, Mv.Config.smtp_env_configured?())
|
||||||
|> assign(:smtp_host_env_set, Mv.Config.smtp_host_env_set?())
|
|> assign(:smtp_host_env_set, Mv.Config.smtp_host_env_set?())
|
||||||
|> assign(:smtp_port_env_set, Mv.Config.smtp_port_env_set?())
|
|> assign(:smtp_port_env_set, Mv.Config.smtp_port_env_set?())
|
||||||
|
|
@ -607,8 +610,29 @@ defmodule MvWeb.GlobalSettingsLive do
|
||||||
<% end %>
|
<% end %>
|
||||||
</.form>
|
</.form>
|
||||||
</.form_section>
|
</.form_section>
|
||||||
<%!-- OIDC Section --%>
|
<%!-- Authentication: Direct registration + OIDC --%>
|
||||||
<.form_section title={gettext("OIDC (Single Sign-On)")}>
|
<.form_section title={gettext("Authentication")}>
|
||||||
|
<h3 class="font-medium mb-3">{gettext("Direct registration")}</h3>
|
||||||
|
<p class="text-sm text-base-content/70 mb-4">
|
||||||
|
{gettext(
|
||||||
|
"If disabled, users cannot sign up via /register; sign-in and the join form remain available."
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<div class="flex items-center gap-3 mb-6">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="registration-enabled-checkbox"
|
||||||
|
class="checkbox checkbox-sm"
|
||||||
|
checked={@registration_enabled}
|
||||||
|
phx-click="toggle_registration_enabled"
|
||||||
|
aria-label={gettext("Allow direct registration (/register)")}
|
||||||
|
/>
|
||||||
|
<label for="registration-enabled-checkbox" class="cursor-pointer font-medium">
|
||||||
|
{gettext("Allow direct registration (/register)")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="font-medium mb-3">{gettext("OIDC (Single Sign-On)")}</h3>
|
||||||
<%= if @oidc_env_configured do %>
|
<%= if @oidc_env_configured do %>
|
||||||
<p class="text-sm text-base-content/70 mb-4">
|
<p class="text-sm text-base-content/70 mb-4">
|
||||||
{gettext("Some values are set via environment variables. Those fields are read-only.")}
|
{gettext("Some values are set via environment variables. Those fields are read-only.")}
|
||||||
|
|
@ -853,6 +877,7 @@ defmodule MvWeb.GlobalSettingsLive do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(:settings, fresh_settings)
|
|> assign(:settings, fresh_settings)
|
||||||
|
|> assign(:registration_enabled, fresh_settings.registration_enabled != false)
|
||||||
|> assign(:vereinfacht_api_key_set, present?(fresh_settings.vereinfacht_api_key))
|
|> assign(:vereinfacht_api_key_set, present?(fresh_settings.vereinfacht_api_key))
|
||||||
|> assign(:oidc_client_secret_set, Mv.Config.oidc_client_secret_set?())
|
|> assign(:oidc_client_secret_set, Mv.Config.oidc_client_secret_set?())
|
||||||
|> assign(:oidc_configured, Mv.Config.oidc_configured?())
|
|> assign(:oidc_configured, Mv.Config.oidc_configured?())
|
||||||
|
|
@ -889,6 +914,24 @@ defmodule MvWeb.GlobalSettingsLive do
|
||||||
{:noreply, persist_join_form_settings(socket)}
|
{:noreply, persist_join_form_settings(socket)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("toggle_registration_enabled", _params, socket) do
|
||||||
|
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()}
|
||||||
|
|
||||||
|
{:error, _} ->
|
||||||
|
{:noreply, put_flash(socket, :error, gettext("Failed to update setting."))}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("toggle_add_field_dropdown", _params, socket) do
|
def handle_event("toggle_add_field_dropdown", _params, socket) do
|
||||||
{:noreply,
|
{:noreply,
|
||||||
|
|
|
||||||
55
lib/mv_web/plugs/registration_enabled.ex
Normal file
55
lib/mv_web/plugs/registration_enabled.ex
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
defmodule MvWeb.Plugs.RegistrationEnabled do
|
||||||
|
@moduledoc """
|
||||||
|
When direct registration is disabled in settings:
|
||||||
|
- GET /register is redirected to /sign-in with a flash message.
|
||||||
|
Puts registration_enabled from settings into session for /sign-in and /register
|
||||||
|
so the sign-in LiveView can show or hide the register link.
|
||||||
|
"""
|
||||||
|
import Plug.Conn
|
||||||
|
import Phoenix.Controller
|
||||||
|
|
||||||
|
alias Mv.Membership
|
||||||
|
|
||||||
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
def call(conn, _opts) do
|
||||||
|
conn
|
||||||
|
|> maybe_redirect_register()
|
||||||
|
|> maybe_put_registration_enabled_in_session()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_redirect_register(conn) do
|
||||||
|
if conn.request_path == "/register" and conn.method == "GET" do
|
||||||
|
case Membership.get_settings() do
|
||||||
|
{:ok, %{registration_enabled: true}} ->
|
||||||
|
conn
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, get_flash_message(conn))
|
||||||
|
|> redirect(to: "/sign-in")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_flash_message(_conn) do
|
||||||
|
Gettext.dgettext(MvWeb.Gettext, "default", "Registration is disabled.")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_registration_enabled_in_session(conn) do
|
||||||
|
if conn.request_path in ["/sign-in", "/register"] do
|
||||||
|
enabled =
|
||||||
|
case Membership.get_settings() do
|
||||||
|
{:ok, %{registration_enabled: enabled?}} -> enabled?
|
||||||
|
_ -> true
|
||||||
|
end
|
||||||
|
|
||||||
|
put_session(conn, "registration_enabled", enabled)
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -16,6 +16,7 @@ defmodule MvWeb.Router do
|
||||||
plug :set_locale
|
plug :set_locale
|
||||||
plug MvWeb.Plugs.CheckPagePermission
|
plug MvWeb.Plugs.CheckPagePermission
|
||||||
plug MvWeb.Plugs.JoinFormEnabled
|
plug MvWeb.Plugs.JoinFormEnabled
|
||||||
|
plug MvWeb.Plugs.RegistrationEnabled
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :api do
|
pipeline :api do
|
||||||
|
|
|
||||||
|
|
@ -3867,3 +3867,33 @@ msgstr "Vielen Dank"
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "You will receive an email once your application has been reviewed."
|
msgid "You will receive an email once your application has been reviewed."
|
||||||
msgstr "Du erhältst eine E-Mail, sobald dein Antrag geprüft wurde."
|
msgstr "Du erhältst eine E-Mail, sobald dein Antrag geprüft wurde."
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Allow direct registration (/register)"
|
||||||
|
msgstr "Direkte Registrierung erlauben (/register)"
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Authentication"
|
||||||
|
msgstr "Anmeldung"
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Direct registration"
|
||||||
|
msgstr "Direkte Registrierung"
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Failed to update setting."
|
||||||
|
msgstr "Einstellung konnte nicht gespeichert werden."
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
|
||||||
|
msgstr "Wenn deaktiviert, können sich Nutzer*innen nicht über /register anmelden; Anmeldung und Beitrittsformular bleiben verfügbar."
|
||||||
|
|
||||||
|
#: lib/mv_web/plugs/registration_enabled.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Registration is disabled."
|
||||||
|
msgstr "Die Registrierung ist deaktiviert."
|
||||||
|
|
|
||||||
|
|
@ -3867,3 +3867,33 @@ msgstr ""
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "You will receive an email once your application has been reviewed."
|
msgid "You will receive an email once your application has been reviewed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Allow direct registration (/register)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Authentication"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Direct registration"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Failed to update setting."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/mv_web/plugs/registration_enabled.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Registration is disabled."
|
||||||
|
msgstr ""
|
||||||
|
|
|
||||||
|
|
@ -3867,3 +3867,33 @@ msgstr "Thank you"
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "You will receive an email once your application has been reviewed."
|
msgid "You will receive an email once your application has been reviewed."
|
||||||
msgstr "You will receive an email once your application has been reviewed."
|
msgstr "You will receive an email once your application has been reviewed."
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Allow direct registration (/register)"
|
||||||
|
msgstr "Allow direct registration (/register)"
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Authentication"
|
||||||
|
msgstr "Authentication"
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Direct registration"
|
||||||
|
msgstr "Direct registration"
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Failed to update setting."
|
||||||
|
msgstr "Failed to update setting."
|
||||||
|
|
||||||
|
#: lib/mv_web/live/global_settings_live.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
|
||||||
|
msgstr "If disabled, users cannot sign up via /register; sign-in and the join form remain available."
|
||||||
|
|
||||||
|
#: lib/mv_web/plugs/registration_enabled.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Registration is disabled."
|
||||||
|
msgstr "Registration is disabled."
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
defmodule Mv.Repo.Migrations.AddRegistrationEnabledToSettings do
|
||||||
|
@moduledoc """
|
||||||
|
Adds registration_enabled flag to settings. When false, direct registration
|
||||||
|
via /register is disabled; sign-in and join form remain available.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:settings) do
|
||||||
|
add :registration_enabled, :boolean, default: true, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:settings) do
|
||||||
|
remove :registration_enabled
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -4,6 +4,8 @@ defmodule MvWeb.AuthControllerTest do
|
||||||
import Phoenix.ConnTest
|
import Phoenix.ConnTest
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
|
|
||||||
|
alias Mv.Membership
|
||||||
|
|
||||||
# Helper to create an unauthenticated conn (preserves sandbox metadata)
|
# Helper to create an unauthenticated conn (preserves sandbox metadata)
|
||||||
defp build_unauthenticated_conn(authenticated_conn) do
|
defp build_unauthenticated_conn(authenticated_conn) do
|
||||||
# Create new conn but preserve sandbox metadata for database access
|
# Create new conn but preserve sandbox metadata for database access
|
||||||
|
|
@ -169,6 +171,23 @@ defmodule MvWeb.AuthControllerTest do
|
||||||
assert html =~ "length must be greater than or equal to 8"
|
assert html =~ "length must be greater than or equal to 8"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "when registration is disabled, sign-in page does not show Need an account? toggle", %{
|
||||||
|
conn: authenticated_conn
|
||||||
|
} do
|
||||||
|
{:ok, settings} = Membership.get_settings()
|
||||||
|
original = Map.get(settings, :registration_enabled, true)
|
||||||
|
{:ok, _} = Membership.update_settings(settings, %{registration_enabled: false})
|
||||||
|
|
||||||
|
try do
|
||||||
|
conn = build_unauthenticated_conn(authenticated_conn)
|
||||||
|
{:ok, _view, html} = live(conn, ~p"/sign-in")
|
||||||
|
refute html =~ "Need an account?"
|
||||||
|
after
|
||||||
|
{:ok, s} = Membership.get_settings()
|
||||||
|
Membership.update_settings(s, %{registration_enabled: original})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Access control
|
# Access control
|
||||||
test "unauthenticated user accessing protected route gets redirected to sign-in", %{
|
test "unauthenticated user accessing protected route gets redirected to sign-in", %{
|
||||||
conn: authenticated_conn
|
conn: authenticated_conn
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue