mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2026-03-17 22:36:17 +00:00
add session expiration fields to auth method form and model
This commit is contained in:
@@ -163,6 +163,18 @@ class AuthMethodForm(forms.ModelForm):
|
|||||||
required=False,
|
required=False,
|
||||||
help_text=_('Enter a 6-digit PIN generated by your authenticator app to validate the secret.')
|
help_text=_('Enter a 6-digit PIN generated by your authenticator app to validate the secret.')
|
||||||
)
|
)
|
||||||
|
session_expiration_value = forms.IntegerField(
|
||||||
|
label=_('Session Expiration'),
|
||||||
|
min_value=1,
|
||||||
|
required=False,
|
||||||
|
initial=12,
|
||||||
|
)
|
||||||
|
session_expiration_unit = forms.ChoiceField(
|
||||||
|
label=_('Unit'),
|
||||||
|
choices=[('hours', _('Hour(s)')), ('days', _('Day(s)'))],
|
||||||
|
required=False,
|
||||||
|
initial='hours',
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AuthMethod
|
model = AuthMethod
|
||||||
@@ -185,6 +197,13 @@ class AuthMethodForm(forms.ModelForm):
|
|||||||
|
|
||||||
if self.instance and self.instance.pk:
|
if self.instance and self.instance.pk:
|
||||||
self.fields['auth_type'].disabled = True
|
self.fields['auth_type'].disabled = True
|
||||||
|
exp_min = self.instance.session_expiration_minutes
|
||||||
|
if exp_min % 1440 == 0:
|
||||||
|
self.initial['session_expiration_value'] = exp_min // 1440
|
||||||
|
self.initial['session_expiration_unit'] = 'days'
|
||||||
|
else:
|
||||||
|
self.initial['session_expiration_value'] = max(1, round(exp_min / 60))
|
||||||
|
self.initial['session_expiration_unit'] = 'hours'
|
||||||
|
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.layout = Layout(
|
self.helper.layout = Layout(
|
||||||
@@ -207,6 +226,11 @@ class AuthMethodForm(forms.ModelForm):
|
|||||||
Div('oidc_client_secret', css_class='col-xl-6'),
|
Div('oidc_client_secret', css_class='col-xl-6'),
|
||||||
css_class='row oidc-group'
|
css_class='row oidc-group'
|
||||||
),
|
),
|
||||||
|
Div(
|
||||||
|
Div('session_expiration_value', css_class='col-xl-6'),
|
||||||
|
Div('session_expiration_unit', css_class='col-xl-6'),
|
||||||
|
css_class='row expiration-group'
|
||||||
|
),
|
||||||
Div(
|
Div(
|
||||||
Div(
|
Div(
|
||||||
Submit('submit', _('Save'), css_class='btn btn-primary'),
|
Submit('submit', _('Save'), css_class='btn btn-primary'),
|
||||||
@@ -260,8 +284,24 @@ class AuthMethodForm(forms.ModelForm):
|
|||||||
if cleaned_data.get('totp_pin'):
|
if cleaned_data.get('totp_pin'):
|
||||||
self.add_error('totp_pin', _('TOTP validation PIN must be empty for OIDC authentication.'))
|
self.add_error('totp_pin', _('TOTP validation PIN must be empty for OIDC authentication.'))
|
||||||
|
|
||||||
|
if auth_type in ('local_password', 'oidc'):
|
||||||
|
value = cleaned_data.get('session_expiration_value') or 12
|
||||||
|
unit = cleaned_data.get('session_expiration_unit') or 'hours'
|
||||||
|
if unit == 'days':
|
||||||
|
cleaned_data['_session_expiration_minutes'] = value * 1440
|
||||||
|
else:
|
||||||
|
cleaned_data['_session_expiration_minutes'] = value * 60
|
||||||
|
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
instance = super().save(commit=False)
|
||||||
|
if instance.auth_type in ('local_password', 'oidc'):
|
||||||
|
instance.session_expiration_minutes = self.cleaned_data.get('_session_expiration_minutes', 720)
|
||||||
|
if commit:
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
class GatekeeperIPAddressForm(forms.ModelForm):
|
class GatekeeperIPAddressForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = GatekeeperIPAddress
|
model = GatekeeperIPAddress
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gatekeeper', '0007_remove_authmethod_totp_before_auth_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='authmethod',
|
||||||
|
name='session_expiration_minutes',
|
||||||
|
field=models.PositiveIntegerField(default=720, help_text='Session expiration time in minutes (only for Local Password and OIDC)'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.12 on 2026-03-15 20:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gatekeeper', '0008_authmethod_session_expiration_minutes'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='authmethod',
|
||||||
|
name='session_expiration_minutes',
|
||||||
|
field=models.PositiveIntegerField(default=720, help_text='Session expiration time in minutes'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
gatekeeper/migrations/0010_alter_gatekeeperuser_email.py
Normal file
18
gatekeeper/migrations/0010_alter_gatekeeperuser_email.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.12 on 2026-03-15 22:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gatekeeper', '0009_alter_authmethod_session_expiration_minutes'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='gatekeeperuser',
|
||||||
|
name='email',
|
||||||
|
field=models.EmailField(blank=True, max_length=254),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -16,6 +16,12 @@ class AuthMethod(models.Model):
|
|||||||
# TOTP-specific fields
|
# TOTP-specific fields
|
||||||
totp_secret = models.CharField(max_length=255, blank=True, help_text=_("Shared/global TOTP secret key"))
|
totp_secret = models.CharField(max_length=255, blank=True, help_text=_("Shared/global TOTP secret key"))
|
||||||
|
|
||||||
|
# Session expiration (Local Password and OIDC only)
|
||||||
|
session_expiration_minutes = models.PositiveIntegerField(
|
||||||
|
default=720,
|
||||||
|
help_text=_("Session expiration time in minutes")
|
||||||
|
)
|
||||||
|
|
||||||
# OIDC-specific fields
|
# OIDC-specific fields
|
||||||
oidc_provider = models.CharField(max_length=64, blank=True)
|
oidc_provider = models.CharField(max_length=64, blank=True)
|
||||||
oidc_client_id = models.CharField(max_length=255, blank=True)
|
oidc_client_id = models.CharField(max_length=255, blank=True)
|
||||||
@@ -64,7 +70,7 @@ class AuthMethodAllowedEmail(models.Model):
|
|||||||
|
|
||||||
class GatekeeperUser(models.Model):
|
class GatekeeperUser(models.Model):
|
||||||
username = models.SlugField(max_length=64, unique=True)
|
username = models.SlugField(max_length=64, unique=True)
|
||||||
email = models.EmailField(unique=True, blank=True)
|
email = models.EmailField(blank=True)
|
||||||
password = models.CharField(blank=True, max_length=250, help_text=_("Password for local authentication (leave blank if not using)"))
|
password = models.CharField(blank=True, max_length=250, help_text=_("Password for local authentication (leave blank if not using)"))
|
||||||
totp_secret = models.CharField(max_length=255, blank=True, help_text=_("Per-user TOTP secret key"))
|
totp_secret = models.CharField(max_length=255, blank=True, help_text=_("Per-user TOTP secret key"))
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ def view_manage_auth_method(request):
|
|||||||
<p>Users will authenticate using a standard username and password stored locally. Only one of this type can be created.</p>
|
<p>Users will authenticate using a standard username and password stored locally. Only one of this type can be created.</p>
|
||||||
|
|
||||||
<h5>OIDC (OpenID Connect)</h5>
|
<h5>OIDC (OpenID Connect)</h5>
|
||||||
<p>Users will authenticate via an external identity provider (like Keycloak, Google, or Authelia). Requires Provider URL, Client ID, and Client Secret.</p>
|
<p>Users will authenticate via an external identity provider (like Keycloak or Google). Requires Provider URL, Client ID, and Client Secret.</p>
|
||||||
|
|
||||||
<h5>TOTP (Time-Based One-Time Password)</h5>
|
<h5>TOTP (Time-Based One-Time Password)</h5>
|
||||||
<p>Users will need to enter a rotating token from an authenticator app. If a user does not have a personal TOTP configured, the <strong>Global TOTP Secret</strong> will be used instead. </p>
|
<p>Users will need to enter a rotating token from an authenticator app. If a user does not have a personal TOTP configured, the <strong>Global TOTP Secret</strong> will be used instead. </p>
|
||||||
|
|||||||
@@ -41,18 +41,22 @@
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
function toggleFields() {
|
function toggleFields() {
|
||||||
var authType = $('#id_auth_type').val();
|
var authType = $('#id_auth_type').val();
|
||||||
if (authType === 'local_password' || authType === 'ip_address') {
|
if (authType === 'local_password') {
|
||||||
$('.totp-group').hide();
|
$('.totp-group').hide();
|
||||||
$('.oidc-group').hide();
|
$('.oidc-group').hide();
|
||||||
|
$('.expiration-group').show();
|
||||||
} else if (authType === 'totp') {
|
} else if (authType === 'totp') {
|
||||||
$('.totp-group').show();
|
$('.totp-group').show();
|
||||||
$('.oidc-group').hide();
|
$('.oidc-group').hide();
|
||||||
|
$('.expiration-group').hide();
|
||||||
} else if (authType === 'oidc') {
|
} else if (authType === 'oidc') {
|
||||||
$('.totp-group').hide();
|
$('.totp-group').hide();
|
||||||
$('.oidc-group').show();
|
$('.oidc-group').show();
|
||||||
|
$('.expiration-group').show();
|
||||||
} else {
|
} else {
|
||||||
$('.totp-group').hide();
|
$('.totp-group').hide();
|
||||||
$('.oidc-group').hide();
|
$('.oidc-group').hide();
|
||||||
|
$('.expiration-group').hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user