Update peer management view and forms.

This commit is contained in:
Eduardo Silva
2026-01-26 13:57:31 -03:00
parent 666ea0402d
commit 5b21f24b7c
4 changed files with 152 additions and 126 deletions

View File

@@ -7,43 +7,65 @@
<div class="card-header">
<h3 class="card-title">{% trans 'Peer Configuration' %}</h3>
</div>
<form method="post">
{% csrf_token %}
<div class="card-body row">
<div class="card-body">
<div class="row">
<div class="col-lg-6">
<!-- Name -->
<div class="form-group">
<label for="{{ form.name.id_for_label }}">{{ form.name.label }}</label>
<input type="text" class="form-control" id="{{ form.name.id_for_label }}" name="{{ form.name.html_name }}" placeholder="{% trans 'Enter Name' %}" value="{{ form.name.value|default_if_none:'' }}">
<div class="form-group border-bottom pb-3">
<label>{% trans 'Name' %}</label>
<div class="d-flex justify-content-between align-items-center">
<span>{{ current_peer.name|default:"-" }}</span>
<a href="{% url 'wireguard_peer_edit_field' %}?peer={{ current_peer.uuid }}&group=name"
class="btn btn-tool">
<i class="fas fa-pen"></i>
</a>
</div>
</div>
<!-- Persistent Keepalive -->
<div class="form-group">
<label for="{{ form.persistent_keepalive.id_for_label }}">{{ form.persistent_keepalive.label }}</label>
<input type="number" class="form-control" id="{{ form.persistent_keepalive.id_for_label }}" name="{{ form.persistent_keepalive.html_name }}" placeholder="{% trans 'Persistent Keepalive' %}" value="{{ form.persistent_keepalive.value|default_if_none:'' }}" required>
</div>
<!-- Public Key -->
<div class="form-group">
<label for="{{ form.public_key.id_for_label }}">{{ form.public_key.label }}</label>
<input type="text" class="form-control" id="{{ form.public_key.id_for_label }}" name="{{ form.public_key.html_name }}" placeholder="{% trans 'Public Key' %}" value="{{ form.public_key.value|default_if_none:'' }}" required>
</div>
<!-- Private Key -->
<div class="form-group">
<label for="{{ form.private_key.id_for_label }}">{{ form.private_key.label }}</label>
<div class="input-group">
<input type="password" class="form-control" id="{{ form.private_key.id_for_label }}" name="{{ form.private_key.html_name }}" placeholder="{% trans 'Private Key' %}" value="{{ form.private_key.value|default_if_none:'' }}">
<div class="input-group-append">
<button class="btn btn-outline-secondary toggle-password" type="button"><i class="fas fa-eye"></i></button>
</div>
<div class="form-group border-bottom pb-3">
<label>{% trans 'Persistent Keepalive' %}</label>
<div class="d-flex justify-content-between align-items-center">
<span>{{ current_peer.persistent_keepalive }}</span>
<a href="{% url 'wireguard_peer_edit_field' %}?peer={{ current_peer.uuid }}&group=keepalive"
class="btn btn-tool">
<i class="fas fa-pen"></i>
</a>
</div>
</div>
<!-- Pre-Shared Key -->
<!-- Keys Section -->
<div class="form-group">
<label for="{{ form.pre_shared_key.id_for_label }}">{{ form.pre_shared_key.label }}</label>
<input type="text" class="form-control" id="{{ form.pre_shared_key.id_for_label }}" name="{{ form.pre_shared_key.html_name }}" placeholder="{% trans 'Pre-Shared Key' %}" value="{{ form.pre_shared_key.value|default_if_none:'' }}" required>
<div class="d-flex justify-content-between align-items-center mb-2">
<label class="mb-0">{% trans 'Keys' %}</label>
<a href="{% url 'wireguard_peer_edit_field' %}?peer={{ current_peer.uuid }}&group=keys"
class="btn btn-tool">
<i class="fas fa-pen"></i>
</a>
</div>
<dl>
<dt class="small text-muted font-weight-normal">{% trans 'Public Key' %}</dt>
<dd class="text-break">{{ current_peer.public_key }}</dd>
<dt class="small text-muted font-weight-normal">{% trans 'Private Key' %}</dt>
<dd>
{% if current_peer.private_key %}
<code>********************************************</code>
{% else %}
<span class="text-muted">{% trans 'Not set' %}</span>
{% endif %}
</dd>
<dt class="small text-muted font-weight-normal">{% trans 'Pre-Shared Key' %}</dt>
<dd>
{% if current_peer.pre_shared_key %}
<code>********************************************</code>
{% else %}
<span class="text-muted">{% trans 'Not set' %}</span>
{% endif %}
</dd>
</dl>
</div>
</div>
@@ -195,47 +217,18 @@
</div>
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
<a href="/peer/manage_ip_address/?peer={{ current_peer.uuid }}" class="btn btn-outline-primary">{% trans 'Add IP Address' %}</a>
<a class="btn btn-outline-secondary" href="/peer/list/?uuid={{ current_peer.wireguard_instance.uuid }}#peer-{{ current_peer.public_key }}">{% trans 'Back' %}</a>
<a href='javascript:void(0)' class='btn btn-outline-danger' data-command='delete' onclick='openCommandDialog(this)'>{% trans 'Delete Peer' %}</a>
</div>
</form>
</div>
{% endblock %}
{% block custom_page_scripts %}
<script>
document.addEventListener('DOMContentLoaded', function () {
var alertShown = false;
var fieldsToWatch = ['id_public_key', 'id_pre_shared_key', 'id_private_key'];
function showAlert() {
if (!alertShown) {
$(document).Toasts('create', {
class: 'bg-warning',
title: '{% trans 'Action Required!' %}',
body: '{% trans 'When manually updating the "Public Key", "Pre-Shared Key", or "Private Key", please ensure the configuration is correct.' %}',
});
alertShown = true;
}
}
fieldsToWatch.forEach(function(fieldId) {
var field = document.getElementById(fieldId);
if (field) {
field.addEventListener('change', showAlert);
}
});
});
</script>
<script>
function openCommandDialog(element) {
var command = element.getAttribute('data-command');
@@ -247,22 +240,6 @@
}
</script>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('.toggle-password').addEventListener('click', function () {
let passwordInput = document.getElementById('{{ form.private_key.id_for_label }}');
let passStatus = passwordInput.getAttribute('type') === 'password';
passwordInput.setAttribute('type', passStatus ? 'text' : 'password');
this.innerHTML = passStatus ? '<i class="fas fa-eye-slash"></i>' : '<i class="fas fa-eye"></i>';
});
document.getElementById('{{ form.private_key.id_for_label }}').addEventListener('keypress', function () {
this.setAttribute('type', 'text');
});
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function(){
var buttons = document.querySelectorAll('.btn-group a');
@@ -280,23 +257,4 @@ document.addEventListener('DOMContentLoaded', function(){
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var form = document.querySelector('form[method="post"]');
if (form) {
form.addEventListener('submit', function(e) {
var privateKeyField = document.getElementById('{{ form.private_key.id_for_label }}');
if (privateKeyField && privateKeyField.value.trim() === '') {
var confirmed = confirm('{% trans "The private key is empty. The peers configuration file and QR code will be generated without the private key.\n It must be inserted manually when importing.\n\n Do you want to continue?" %}');
if (!confirmed) {
e.preventDefault();
return false;
}
}
});
}
});
</script>
{% endblock %}

View File

@@ -1,21 +1,56 @@
import ipaddress
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Button
from django import forms
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils.translation import gettext_lazy as _
from wireguard.models import NETMASK_CHOICES, Peer, PeerAllowedIP
class PeerForm(forms.ModelForm):
class PeerModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.layout = Layout(
*self.Meta.fields,
FormActions(
Submit('save', _('Save'), css_class='btn-primary'),
Button('cancel', _('Back'), css_class='btn-outline-secondary', onclick='window.history.back()')
)
)
class PeerNameForm(PeerModelForm):
name = forms.CharField(label=_('Name'), required=False)
public_key = forms.CharField(label=_('Public Key'), required=True)
private_key = forms.CharField(label=_('Private Key'), required=False)
pre_shared_key = forms.CharField(label=_('Pre-Shared Key'), required=True)
persistent_keepalive = forms.IntegerField(label=_('Persistent Keepalive'), required=True)
class Meta:
model = Peer
fields = ['name', 'public_key', 'private_key', 'pre_shared_key', 'persistent_keepalive']
fields = ['name']
class PeerKeepaliveForm(PeerModelForm):
persistent_keepalive = forms.IntegerField(
label=_('Persistent Keepalive'),
required=True,
validators=[MinValueValidator(1), MaxValueValidator(3600)],
)
class Meta:
model = Peer
fields = ['persistent_keepalive']
class PeerKeysForm(PeerModelForm):
public_key = forms.CharField(label=_('Public Key'), required=True)
private_key = forms.CharField(label=_('Private Key'), required=False)
pre_shared_key = forms.CharField(label=_('Pre-Shared Key'), required=True)
class Meta:
model = Peer
fields = ['public_key', 'private_key', 'pre_shared_key']
class PeerAllowedIPForm(forms.ModelForm):

View File

@@ -14,7 +14,7 @@ from user_manager.models import UserAcl
from wgwadmlibrary.tools import check_sort_order_conflict, deduplicate_sort_order, default_sort_peers, \
user_allowed_instances, user_allowed_peers, user_has_access_to_instance, user_has_access_to_peer
from wireguard.models import Peer, PeerAllowedIP, WireGuardInstance
from wireguard_peer.forms import PeerAllowedIPForm, PeerForm
from wireguard_peer.forms import PeerAllowedIPForm, PeerNameForm, PeerKeepaliveForm, PeerKeysForm
def generate_peer_default(wireguard_instance):
@@ -184,10 +184,6 @@ def view_wireguard_peer_create(request):
@login_required
def view_wireguard_peer_manage(request):
if request.method == 'POST':
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=30).exists():
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
else:
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=20).exists():
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
user_acl = get_object_or_404(UserAcl, user=request.user)
@@ -197,6 +193,8 @@ def view_wireguard_peer_manage(request):
raise Http404
current_instance = current_peer.wireguard_instance
if request.GET.get('action') == 'delete':
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=30).exists():
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
if request.GET.get('confirmation') == 'delete':
current_peer.wireguard_instance.pending_changes = True
current_peer.wireguard_instance.save()
@@ -206,28 +204,62 @@ def view_wireguard_peer_manage(request):
else:
messages.warning(request, _('Error deleting peer|Invalid confirmation message. Type "delete" to confirm.'))
return redirect('/peer/manage/?peer=' + str(current_peer.uuid))
page_title = _('Update Peer: ') + str(current_peer)
page_title = _('Peer Configuration: ') + str(current_peer)
peer_ip_list = current_peer.peerallowedip_set.filter(config_file='server').order_by('priority')
peer_client_ip_list = current_peer.peerallowedip_set.filter(config_file='client').order_by('priority')
if request.method == 'POST':
form = PeerForm(request.POST, instance=current_peer)
if form.is_valid():
form.save()
messages.success(request, _('Peer updated|Peer updated successfully.'))
current_peer.wireguard_instance.pending_changes = True
current_peer.wireguard_instance.save()
return redirect('/peer/list/?uuid=' + str(current_peer.wireguard_instance.uuid))
else:
form = PeerForm(instance=current_peer)
context = {
'page_title': page_title, 'current_instance': current_instance, 'current_peer': current_peer, 'form': form,
'page_title': page_title, 'current_instance': current_instance, 'current_peer': current_peer,
'peer_ip_list': peer_ip_list, 'peer_client_ip_list': peer_client_ip_list
}
return render(request, 'wireguard/wireguard_manage_peer.html', context)
@login_required
def view_wireguard_peer_edit_field(request):
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=30).exists():
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
user_acl = get_object_or_404(UserAcl, user=request.user)
current_peer = get_object_or_404(Peer, uuid=request.GET.get('peer'))
if not user_has_access_to_peer(user_acl, current_peer):
raise Http404
group = request.GET.get('group')
form_classes = {
'name': PeerNameForm,
'keepalive': PeerKeepaliveForm,
'keys': PeerKeysForm
}
if group not in form_classes:
raise Http404
FormClass = form_classes[group]
form = FormClass(request.POST or None, instance=current_peer)
if form.is_valid():
form.save()
current_peer.wireguard_instance.pending_changes = True
current_peer.wireguard_instance.save()
messages.success(request, _('Peer updated|Peer updated successfully.'))
return redirect('/peer/manage/?peer=' + str(current_peer.uuid))
page_title = _('Edit Peer')
if group == 'name':
page_title = _('Edit Peer Name')
elif group == 'keepalive':
page_title = _('Edit Keepalive')
elif group == 'keys':
page_title = _('Edit Keys')
context = {
'page_title': page_title,
'form': form,
}
return render(request, 'generic_form.html', context)
def view_manage_ip_address(request):
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=30).exists():
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})

View File

@@ -39,7 +39,7 @@ from wgrrd.views import view_rrd_graph
from wireguard.views import view_apply_db_patches, view_wireguard_manage_instance, view_wireguard_status, \
view_server_list, view_server_detail
from wireguard_peer.views import view_manage_ip_address, view_wireguard_peer_list, view_wireguard_peer_manage, \
view_wireguard_peer_sort, view_apply_route_template, view_wireguard_peer_create
view_wireguard_peer_sort, view_apply_route_template, view_wireguard_peer_create, view_wireguard_peer_edit_field
from wireguard_tools.views import download_config_or_qrcode, export_wireguard_configs, restart_wireguard_interfaces
urlpatterns = [
@@ -57,6 +57,7 @@ urlpatterns = [
path('peer/sort/', view_wireguard_peer_sort, name='wireguard_peer_sort'),
path('peer/manage/', view_wireguard_peer_manage, name='wireguard_peer_manage'),
path('peer/create/', view_wireguard_peer_create, name='wireguard_peer_create'),
path('peer/edit/', view_wireguard_peer_edit_field, name='wireguard_peer_edit_field'),
path('peer/apply_route_template/', view_apply_route_template, name='apply_route_template'),
path('peer/manage_ip_address/', view_manage_ip_address, name='manage_ip_address'),
path('rrd/graph/', view_rrd_graph, name='rrd_graph'),