mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2025-04-19 00:45:16 +00:00
VPN invite settings form and validation logic
This commit is contained in:
parent
0277892305
commit
dc85a76715
@ -1,8 +1,9 @@
|
|||||||
from crispy_forms.templatetags.crispy_forms_field import css_class
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import InviteSettings
|
from django.core.exceptions import ValidationError
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Layout, Row, Column, Submit, HTML
|
from crispy_forms.layout import Layout, Row, Column, Submit, HTML
|
||||||
|
from crispy_forms.templatetags.crispy_forms_field import css_class
|
||||||
|
from .models import InviteSettings
|
||||||
|
|
||||||
|
|
||||||
class InviteSettingsForm(forms.ModelForm):
|
class InviteSettingsForm(forms.ModelForm):
|
||||||
@ -148,7 +149,7 @@ class InviteSettingsForm(forms.ModelForm):
|
|||||||
Row(
|
Row(
|
||||||
Column(
|
Column(
|
||||||
Submit('submit', 'Save', css_class='btn btn-success'),
|
Submit('submit', 'Save', css_class='btn btn-success'),
|
||||||
HTML(' <a class="btn btn-secondary" href="/configurations/list/">Back</a> '),
|
HTML(' <a class="btn btn-secondary" href="/vpn_invite/">Back</a> '),
|
||||||
css_class='col-md-12'
|
css_class='col-md-12'
|
||||||
),
|
),
|
||||||
css_class='form-row'
|
css_class='form-row'
|
||||||
@ -156,7 +157,6 @@ class InviteSettingsForm(forms.ModelForm):
|
|||||||
css_class='col-xl-6'),
|
css_class='col-xl-6'),
|
||||||
Column(
|
Column(
|
||||||
HTML("<h3>Message templates</h3>"),
|
HTML("<h3>Message templates</h3>"),
|
||||||
Column( css_class='form-group col-md-12 mb-0'),
|
|
||||||
Row(
|
Row(
|
||||||
Column('download_instructions', css_class='form-group col-md-12 mb-0'),
|
Column('download_instructions', css_class='form-group col-md-12 mb-0'),
|
||||||
css_class='form-row'
|
css_class='form-row'
|
||||||
@ -188,3 +188,68 @@ class InviteSettingsForm(forms.ModelForm):
|
|||||||
css_class='col-xl-6'),
|
css_class='col-xl-6'),
|
||||||
css_class='row'),
|
css_class='row'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
|
||||||
|
# Validate invite_url: it must start with 'https://' and end with '/invite/'
|
||||||
|
invite_url = cleaned_data.get('invite_url')
|
||||||
|
if invite_url:
|
||||||
|
if not invite_url.startswith("https://"):
|
||||||
|
self.add_error('invite_url', "Invite URL must start with 'https://'.")
|
||||||
|
if not invite_url.endswith("/invite/"):
|
||||||
|
self.add_error('invite_url', "Invite URL must end with '/invite/'.")
|
||||||
|
|
||||||
|
# Validate invite_expiration: must be between 1 and 1440 minutes
|
||||||
|
invite_expiration = cleaned_data.get('invite_expiration')
|
||||||
|
if invite_expiration is not None:
|
||||||
|
if invite_expiration < 1 or invite_expiration > 1440:
|
||||||
|
self.add_error('invite_expiration', "Expiration (minutes) must be between 1 and 1440.")
|
||||||
|
|
||||||
|
# Validate default_password based on enforce_random_password flag
|
||||||
|
default_password = cleaned_data.get('default_password', '')
|
||||||
|
enforce_random_password = cleaned_data.get('enforce_random_password')
|
||||||
|
random_password_length = cleaned_data.get('random_password_length')
|
||||||
|
if enforce_random_password is True:
|
||||||
|
if default_password:
|
||||||
|
self.add_error('default_password',
|
||||||
|
"Default password must not be provided when random password is enabled.")
|
||||||
|
if random_password_length < 6:
|
||||||
|
self.add_error('random_password_length', "Random password length must be at least 6 characters.")
|
||||||
|
else:
|
||||||
|
# When random password is disabled, default password must be provided and have at least 6 characters.
|
||||||
|
if not default_password:
|
||||||
|
self.add_error('default_password',
|
||||||
|
"Default password must be provided when random password is disabled.")
|
||||||
|
elif len(default_password) < 6:
|
||||||
|
self.add_error('default_password', "Default password must be at least 6 characters long.")
|
||||||
|
|
||||||
|
# Validate download buttons: if enabled, the respective text and url fields must not be blank.
|
||||||
|
for i in range(1, 6):
|
||||||
|
enabled = cleaned_data.get(f'download_{i}_enabled')
|
||||||
|
label = (cleaned_data.get(f'download_{i}_label') or '').strip()
|
||||||
|
url = (cleaned_data.get(f'download_{i}_url') or '').strip()
|
||||||
|
if enabled:
|
||||||
|
if not label:
|
||||||
|
self.add_error(f'download_{i}_label',
|
||||||
|
"Text field must not be empty when download button is enabled.")
|
||||||
|
if not url:
|
||||||
|
self.add_error(f'download_{i}_url', "URL field must not be empty when download button is enabled.")
|
||||||
|
|
||||||
|
# Validate that default_password is not contained in any message templates or the subject
|
||||||
|
message_fields = ['invite_text_body', 'invite_email_subject', 'invite_email_body', 'invite_whatsapp_body']
|
||||||
|
if default_password:
|
||||||
|
for field in message_fields:
|
||||||
|
content = cleaned_data.get(field, '')
|
||||||
|
if default_password in content:
|
||||||
|
self.add_error('default_password',
|
||||||
|
f"Default password must not be contained in {field.replace('_', ' ')}.")
|
||||||
|
|
||||||
|
# Validate that all message templates include the placeholder '{invite_url}'
|
||||||
|
for field in message_fields:
|
||||||
|
if field != 'invite_email_subject':
|
||||||
|
content = cleaned_data.get(field, '')
|
||||||
|
if '{invite_url}' not in content:
|
||||||
|
self.add_error(field, "The template must include the placeholder '{invite_url}'.")
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.5 on 2025-02-27 19:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('vpn_invite', '0004_alter_invitesettings_required_user_level'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='invitesettings',
|
||||||
|
name='download_1_label',
|
||||||
|
field=models.CharField(blank=True, default='iOS', max_length=32, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 5.1.5 on 2025-02-28 00:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('vpn_invite', '0005_alter_invitesettings_download_1_label'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='invitesettings',
|
||||||
|
name='invite_email_body',
|
||||||
|
field=models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}\n\nThis link expires in {expire_minutes} minutes.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='invitesettings',
|
||||||
|
name='invite_text_body',
|
||||||
|
field=models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}\n\nThis link expires in {expire_minutes} minutes.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='invitesettings',
|
||||||
|
name='invite_whatsapp_body',
|
||||||
|
field=models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}\n\nThis link expires in {expire_minutes} minutes.'),
|
||||||
|
),
|
||||||
|
]
|
@ -18,7 +18,7 @@ class InviteSettings(models.Model):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
invite_expiration = models.IntegerField(default=30) # minutes
|
invite_expiration = models.IntegerField(default=30) # minutes
|
||||||
download_1_label = models.CharField(max_length=32, default='iPhone', blank=True, null=True)
|
download_1_label = models.CharField(max_length=32, default='iOS', blank=True, null=True)
|
||||||
download_2_label = models.CharField(max_length=32, default='Android', blank=True, null=True)
|
download_2_label = models.CharField(max_length=32, default='Android', blank=True, null=True)
|
||||||
download_3_label = models.CharField(max_length=32, default='Windows', blank=True, null=True)
|
download_3_label = models.CharField(max_length=32, default='Windows', blank=True, null=True)
|
||||||
download_4_label = models.CharField(max_length=32, default='macOS', blank=True, null=True)
|
download_4_label = models.CharField(max_length=32, default='macOS', blank=True, null=True)
|
||||||
@ -42,13 +42,13 @@ class InviteSettings(models.Model):
|
|||||||
|
|
||||||
invite_url = models.URLField(default='')
|
invite_url = models.URLField(default='')
|
||||||
|
|
||||||
invite_text_body = models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}. The link expires in {expire_minutes} minutes.')
|
invite_text_body = models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}\n\nThis link expires in {expire_minutes} minutes.')
|
||||||
|
|
||||||
invite_email_subject = models.CharField(max_length=64, default='WireGuard VPN Invite', blank=True, null=True)
|
invite_email_subject = models.CharField(max_length=64, default='WireGuard VPN Invite', blank=True, null=True)
|
||||||
invite_email_body = models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}. The link expires in {expire_minutes} minutes.')
|
invite_email_body = models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}\n\nThis link expires in {expire_minutes} minutes.')
|
||||||
invite_email_enabled = models.BooleanField(default=True)
|
invite_email_enabled = models.BooleanField(default=True)
|
||||||
|
|
||||||
invite_whatsapp_body = models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}. The link expires in {expire_minutes} minutes.')
|
invite_whatsapp_body = models.TextField(default='Here is your WireGuard VPN invite link: {invite_url}\n\nThis link expires in {expire_minutes} minutes.')
|
||||||
invite_whatsapp_enabled = models.BooleanField(default=True)
|
invite_whatsapp_enabled = models.BooleanField(default=True)
|
||||||
|
|
||||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
|
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
|
||||||
|
@ -4,10 +4,12 @@ from user_manager.models import UserAcl
|
|||||||
from .models import InviteSettings, PeerInvite
|
from .models import InviteSettings, PeerInvite
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from .forms import InviteSettingsForm
|
||||||
|
from django.contrib import messages
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def view_vpn_invite_settings(request):
|
def view_vpn_invite_list(request):
|
||||||
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
||||||
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
|
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
|
||||||
if request.GET.get('invite') and request.GET.get('action') == 'delete':
|
if request.GET.get('invite') and request.GET.get('action') == 'delete':
|
||||||
@ -26,15 +28,31 @@ def view_vpn_invite_settings(request):
|
|||||||
if invite_settings.invite_url.startswith('http://'):
|
if invite_settings.invite_url.startswith('http://'):
|
||||||
invite_settings.invite_url = invite_settings.invite_url.replace('http://', 'https://')
|
invite_settings.invite_url = invite_settings.invite_url.replace('http://', 'https://')
|
||||||
invite_settings.save()
|
invite_settings.save()
|
||||||
|
|
||||||
peer_invite_list = PeerInvite.objects.all().order_by('invite_expiration')
|
peer_invite_list = PeerInvite.objects.all().order_by('invite_expiration')
|
||||||
peer_invite_list.filter(invite_expiration__lt=timezone.now()).delete()
|
peer_invite_list.filter(invite_expiration__lt=timezone.now()).delete()
|
||||||
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'page_title': 'VPN Invite',
|
'page_title': 'VPN Invite',
|
||||||
'peer_invite_list': peer_invite_list,
|
'peer_invite_list': peer_invite_list,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'vpn_invite/invite_settings.html', context=data)
|
return render(request, 'vpn_invite/invite_settings.html', context=data)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_vpn_invite_settings(request):
|
||||||
|
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
||||||
|
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
|
||||||
|
invite_settings = InviteSettings.objects.get(name='default_settings')
|
||||||
|
|
||||||
|
form = InviteSettingsForm(request.POST or None, instance=invite_settings)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
messages.success(request, 'Invite Settings|Settings saved successfully.')
|
||||||
|
return redirect('/vpn_invite/')
|
||||||
|
data = {
|
||||||
|
'invite_settings': invite_settings,
|
||||||
|
'page_title': 'VPN Invite Settings',
|
||||||
|
'form': form,
|
||||||
|
'form_size': 'col-lg-12'
|
||||||
|
}
|
||||||
|
return render(request, 'generic_form.html', context=data)
|
||||||
|
@ -27,7 +27,7 @@ from api.views import wireguard_status, cron_check_updates, cron_update_peer_lat
|
|||||||
from firewall.views import view_redirect_rule_list, manage_redirect_rule, view_firewall_rule_list, manage_firewall_rule, view_manage_firewall_settings, view_generate_iptables_script, view_reset_firewall, view_firewall_migration_required
|
from firewall.views import view_redirect_rule_list, manage_redirect_rule, view_firewall_rule_list, manage_firewall_rule, view_manage_firewall_settings, view_generate_iptables_script, view_reset_firewall, view_firewall_migration_required
|
||||||
from dns.views import view_static_host_list, view_manage_static_host, view_manage_dns_settings, view_apply_dns_config
|
from dns.views import view_static_host_list, view_manage_static_host, view_manage_dns_settings, view_apply_dns_config
|
||||||
from wgrrd.views import view_rrd_graph
|
from wgrrd.views import view_rrd_graph
|
||||||
from vpn_invite.views import view_vpn_invite_settings
|
from vpn_invite.views import view_vpn_invite_list, view_vpn_invite_settings
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
@ -68,5 +68,6 @@ urlpatterns = [
|
|||||||
path('firewall/generate_firewall_script/', view_generate_iptables_script, name='generate_iptables_script'),
|
path('firewall/generate_firewall_script/', view_generate_iptables_script, name='generate_iptables_script'),
|
||||||
path('firewall/reset_to_default/', view_reset_firewall, name='reset_firewall'),
|
path('firewall/reset_to_default/', view_reset_firewall, name='reset_firewall'),
|
||||||
path('firewall/migration_required/', view_firewall_migration_required, name='firewall_migration_required'),
|
path('firewall/migration_required/', view_firewall_migration_required, name='firewall_migration_required'),
|
||||||
path('vpn_invite/', view_vpn_invite_settings, name='vpn_invite_settings'),
|
path('vpn_invite/', view_vpn_invite_list, name='vpn_invite_list'),
|
||||||
|
path('vpn_invite/settings/', view_vpn_invite_settings, name='vpn_invite_settings'),
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user