From 06426b3852f154a3bab4c7bb5e65bbffc89942c6 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Sat, 14 Mar 2026 08:52:31 -0300 Subject: [PATCH] add 'Global TOTP Before Authentication' option and update form layouts --- gatekeeper/forms.py | 50 ++++++++++--------- .../0005_authmethod_totp_before_auth.py | 18 +++++++ gatekeeper/models.py | 1 + gatekeeper/views.py | 16 +++--- 4 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 gatekeeper/migrations/0005_authmethod_totp_before_auth.py diff --git a/gatekeeper/forms.py b/gatekeeper/forms.py index 2c809ed..9b2b538 100644 --- a/gatekeeper/forms.py +++ b/gatekeeper/forms.py @@ -27,20 +27,20 @@ class GatekeeperUserForm(forms.ModelForm): self.helper = FormHelper() self.helper.layout = Layout( Div( - Div('username', css_class='col-md-6'), - Div('email', css_class='col-md-6'), + Div('username', css_class='col-xl-6'), + Div('email', css_class='col-xl-6'), css_class='row' ), Div( - Div(Field('password', type='password'), css_class='col-md-6'), - Div('totp_secret', css_class='col-md-6'), + Div(Field('password', type='password'), css_class='col-xl-6'), + Div('totp_secret', css_class='col-xl-6'), css_class='row' ), Div( Div( Submit('submit', _('Save'), css_class='btn btn-primary'), HTML(f'{_("Cancel")}'), - css_class='col-md-12' + css_class='col-xl-12' ), css_class='row' ) @@ -63,18 +63,18 @@ class GatekeeperGroupForm(forms.ModelForm): self.helper = FormHelper() self.helper.layout = Layout( Div( - Div('name', css_class='col-md-12'), + Div('name', css_class='col-xl-12'), css_class='row' ), Div( - Div('users', css_class='col-md-12'), + Div('users', css_class='col-xl-12'), css_class='row' ), Div( Div( Submit('submit', _('Save'), css_class='btn btn-primary'), HTML(f'{_("Cancel")}'), - css_class='col-md-12' + css_class='col-xl-12' ), css_class='row' ) @@ -92,13 +92,14 @@ class AuthMethodForm(forms.ModelForm): class Meta: model = AuthMethod fields = [ - 'name', 'auth_type', 'totp_secret', + 'name', 'auth_type', 'totp_secret', 'totp_before_auth', 'oidc_provider', 'oidc_client_id', 'oidc_client_secret' ] labels = { 'name': _('Name'), 'auth_type': _('Authentication Type'), 'totp_secret': _('Global TOTP Secret'), + 'totp_before_auth': _('Global TOTP Before Authentication'), 'oidc_provider': _('OIDC Provider URL'), 'oidc_client_id': _('OIDC Client ID'), 'oidc_client_secret': _('OIDC Client Secret'), @@ -114,29 +115,30 @@ class AuthMethodForm(forms.ModelForm): self.helper = FormHelper() self.helper.layout = Layout( Div( - Div('name', css_class='col-md-6'), - Div('auth_type', css_class='col-md-6'), + Div('name', css_class='col-xl-6'), + Div('auth_type', css_class='col-xl-6'), css_class='row auth-type-group' ), Div( - Div('totp_secret', css_class='col-md-6'), - Div('totp_pin', css_class='col-md-6'), + Div('totp_secret', css_class='col-xl-6'), + Div('totp_pin', css_class='col-xl-6'), + Div('totp_before_auth', css_class='col-xl-12'), css_class='row totp-group' ), Div( - Div('oidc_provider', css_class='col-md-12'), + Div('oidc_provider', css_class='col-xl-12'), css_class='row oidc-group' ), Div( - Div('oidc_client_id', css_class='col-md-6'), - Div('oidc_client_secret', css_class='col-md-6'), + Div('oidc_client_id', css_class='col-xl-6'), + Div('oidc_client_secret', css_class='col-xl-6'), css_class='row oidc-group' ), Div( Div( Submit('submit', _('Save'), css_class='btn btn-primary'), HTML(f'{_("Cancel")}'), - css_class='col-md-12' + css_class='col-xl-12' ), css_class='row' ) @@ -206,7 +208,7 @@ class GatekeeperIPAddressForm(forms.ModelForm): self.helper = FormHelper() self.helper.layout = Layout( Div( - Div('auth_method', css_class='col-md-12'), + Div('auth_method', css_class='col-xl-12'), css_class='row' ), Div( @@ -223,7 +225,7 @@ class GatekeeperIPAddressForm(forms.ModelForm): Div( Submit('submit', _('Save'), css_class='btn btn-primary'), HTML(f'{_("Cancel")}'), - css_class='col-md-12' + css_class='col-xl-12' ), css_class='row' ) @@ -245,7 +247,7 @@ class AuthMethodAllowedDomainForm(forms.ModelForm): self.helper = FormHelper() self.helper.layout = Layout( Div( - Div('auth_method', css_class='col-md-6'), + Div('auth_method', css_class='col-xl-6'), Div(PrependedText('domain', '@'), css_class='col-xl-6'), css_class='row' ), @@ -253,7 +255,7 @@ class AuthMethodAllowedDomainForm(forms.ModelForm): Div( Submit('submit', _('Save'), css_class='btn btn-primary'), HTML(f'{_("Cancel")}'), - css_class='col-md-12' + css_class='col-xl-12' ), css_class='row' ) @@ -276,15 +278,15 @@ class AuthMethodAllowedEmailForm(forms.ModelForm): self.helper = FormHelper() self.helper.layout = Layout( Div( - Div('auth_method', css_class='col-md-6'), - Div('email', css_class='col-md-6'), + Div('auth_method', css_class='col-xl-6'), + Div('email', css_class='col-xl-6'), css_class='row' ), Div( Div( Submit('submit', _('Save'), css_class='btn btn-primary'), HTML(f'{_("Cancel")}'), - css_class='col-md-12' + css_class='col-xl-12' ), css_class='row' ) diff --git a/gatekeeper/migrations/0005_authmethod_totp_before_auth.py b/gatekeeper/migrations/0005_authmethod_totp_before_auth.py new file mode 100644 index 0000000..ed89914 --- /dev/null +++ b/gatekeeper/migrations/0005_authmethod_totp_before_auth.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.12 on 2026-03-14 11:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gatekeeper', '0004_alter_gatekeeperipaddress_prefix_length'), + ] + + operations = [ + migrations.AddField( + model_name='authmethod', + name='totp_before_auth', + field=models.BooleanField(default=False), + ), + ] diff --git a/gatekeeper/models.py b/gatekeeper/models.py index bd791e5..ea2a524 100644 --- a/gatekeeper/models.py +++ b/gatekeeper/models.py @@ -15,6 +15,7 @@ class AuthMethod(models.Model): # TOTP-specific fields totp_secret = models.CharField(max_length=255, blank=True, help_text=_("Shared/global TOTP secret key")) + totp_before_auth = models.BooleanField(default=False) # OIDC-specific fields oidc_provider = models.CharField(max_length=64, blank=True) diff --git a/gatekeeper/views.py b/gatekeeper/views.py index 4313fbf..567a7cd 100644 --- a/gatekeeper/views.py +++ b/gatekeeper/views.py @@ -173,13 +173,17 @@ def view_manage_auth_method(request): form_description = { 'size': '', 'content': _(''' -
Authentication Types
+

Authentication Types

Select how users will authenticate through this method.

- + +
Local Password
+

Users will authenticate using a standard username and password stored locally. Only one of this type can be created.

+ +
OIDC (OpenID Connect)
+

Users will authenticate via an external identity provider (like Keycloak, Google, or Authelia). Requires Provider URL, Client ID, and Client Secret.

+ +
TOTP (Time-Based One-Time Password)
+

Users will need to enter a rotating token from an authenticator app. Requires setting a Global TOTP Secret.
If Global TOTP Before Authentication is enabled, the PIN is required before the username and password to help combat bruteforce attacks.

''') }