create and manage peer groups

This commit is contained in:
Eduardo Silva 2025-01-20 11:41:02 -03:00
parent 797058b29b
commit eefc573c61
9 changed files with 222 additions and 11 deletions

View File

@ -21,7 +21,6 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% include "user_manager/list_buttons.html" %}
<a href="/user/manage/" class="btn btn-primary">Add User</a>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,4 @@
<a href="/user/manage/" class="btn btn-primary">Add User</a>
<a href="/user/list/" class="btn {% if request.path == '/user/list/' %}btn-outline-primary{% else %}btn-primary{% endif %}">List Users</a>
<a href="/user/peer-group/list/" class="btn {% if request.path == '/user/peer-group/list/' %}btn-outline-primary{% else %}btn-primary{% endif %}">List Peer Groups</a>
<a href="/user/peer-group/manage/" class="btn btn-primary">Add Peer Group</a>

View File

@ -0,0 +1,77 @@
{% extends "base.html" %}
{% block content %}
<div class="container mt-3">
<div class="card card-primary card-outline">
<div class="card-header">
<h3 class="card-title">{{ form.instance.pk|yesno:"Edit Peer Group,Create New Peer Group" }}</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<form method="post">
{% csrf_token %}
<!-- Name -->
<div class="form-group">
<label for="{{ form.name.id_for_label }}">Name</label>
<input type="text" class="form-control" id="{{ form.name.id_for_label }}" name="{{ form.name.html_name }}" placeholder="Enter Name" value="{{ form.name.value|default_if_none:'' }}">
</div>
<!-- Peers -->
<div class="form-group">
<label for="{{ form.peer.id_for_label }}">Peers</label>
<select class="form-control" id="{{ form.peer.id_for_label }}" name="{{ form.peer.html_name }}" multiple>
{% for peer in form.peer.field.queryset %}
<option value="{{ peer.pk }}" {% if peer.pk in form.peer.value %}selected{% endif %}>{{ peer }}</option>
{% endfor %}
</select>
</div>
<!-- Server Instances -->
<div class="form-group">
<label for="{{ form.server_instance.id_for_label }}">WireGuard Instances</label>
<select class="form-control" id="{{ form.server_instance.id_for_label }}" name="{{ form.server_instance.html_name }}" multiple>
{% for instance in form.server_instance.field.queryset %}
<option value="{{ instance.pk }}" {% if instance.pk in form.server_instance.value %}selected{% endif %}>{{ instance }}</option>
{% endfor %}
</select>
</div>
<div>
<button type="submit" class="btn btn-primary">Submit</button>
<a href="/user/peer-group/list/" class="btn btn-outline-secondary">Back</a>
{% if peer_group %}<a href='javascript:void(0)' class='btn btn-outline-danger' data-command='delete' onclick='openCommandDialog(this)'>Delete Peer Group</a>{% endif %}
</div>
</form>
</div>
<div class="col-md-8">
<h5>Peers</h5>
<p>Select which peers can be managed by users with this peer group.</p>
<h5>WireGuard Instances</h5>
<p>All peers in this WireGuard instance can be managed by users with this peer group, including adding or removing peers.</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_page_scripts %}
<script>
function openCommandDialog(element) {
var command = element.getAttribute('data-command');
var confirmation = prompt("Please type '{{ peer_group.name }}' to remove this peer group.");
if (confirmation) {
var url = "?uuid={{ peer_group.uuid }}&action=delete&confirmation=" + encodeURIComponent(confirmation);
window.location.href = url;
}
}
</script>
{% endblock %}

View File

@ -0,0 +1,37 @@
{% extends "base.html" %}
{% block content %}
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Peers</th>
<th>Server Instance</th>
<th></th>
</tr>
</thead>
<tbody>
{% for peer_group in peer_group_list %}
<tr>
<td>{{ peer_group.name }}</td>
<td>
{% for peer in peer_group.peer.all %}
{{ peer }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td>
{% for instance in peer_group.server_instance.all %}
{{ instance }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td style="width: 1%; white-space: nowrap;">
<a href="/user/peer-group/manage/?uuid={{ peer_group.uuid }}" ><i class="far fa-edit"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% include "user_manager/list_buttons.html" %}
{% endblock %}

View File

@ -3,6 +3,7 @@ from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from .models import UserAcl from .models import UserAcl
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from wireguard.models import PeerGroup
class UserAclForm(UserCreationForm): class UserAclForm(UserCreationForm):
@ -44,3 +45,34 @@ class UserAclForm(UserCreationForm):
) )
return user return user
class PeerGroupForm(forms.ModelForm):
class Meta:
model = PeerGroup
fields = ['name', 'peer', 'server_instance']
def __init__(self, *args, **kwargs):
self.user_id = kwargs.pop('user_id', None)
super().__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
name = cleaned_data.get('name')
peers = cleaned_data.get('peer')
server_instances = cleaned_data.get('server_instance')
if PeerGroup.objects.filter(name=name).exclude(pk=self.instance.pk if self.instance else None).exists():
raise ValidationError("A peer group with that name already exists.")
return cleaned_data
def save(self, commit=True):
peer_group = super().save(commit=False)
if commit:
peer_group.save()
return peer_group

View File

@ -5,6 +5,54 @@ from .forms import UserAclForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib import messages from django.contrib import messages
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from wireguard.models import PeerGroup
from .forms import PeerGroupForm
@login_required
def view_peer_group_list(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'})
page_title = 'Peer Group Manager'
peer_group_list = PeerGroup.objects.all().order_by('name')
context = {'page_title': page_title, 'peer_group_list': peer_group_list}
return render(request, 'user_manager/peer_group_list.html', context)
@login_required
def view_peer_group_manage(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'})
peer_group = None
if 'uuid' in request.GET:
peer_group = get_object_or_404(PeerGroup, uuid=request.GET['uuid'])
form = PeerGroupForm(instance=peer_group, user_id=request.user.id)
page_title = 'Edit Peer Group ' + peer_group.name
if request.GET.get('action') == 'delete':
group_name = peer_group.name
if request.GET.get('confirmation') == group_name:
peer_group.delete()
messages.success(request, 'Peer Group deleted|The peer group ' + group_name + ' has been deleted.')
return redirect('/user/peer-group/list/')
return redirect('/user/peer-group/list/')
else:
form = PeerGroupForm(user_id=request.user.id)
page_title = 'Add Peer Group'
if request.method == 'POST':
if peer_group:
form = PeerGroupForm(request.POST, instance=peer_group, user_id=request.user.id)
else:
form = PeerGroupForm(request.POST, user_id=request.user.id)
if form.is_valid():
peer_group = form.save()
form.save_m2m()
return redirect('/user/peer-group/list/')
context = {'page_title': page_title, 'form': form, 'peer_group': peer_group}
return render(request, 'user_manager/manage_peer_group.html', context)
@login_required @login_required
def view_user_list(request): def view_user_list(request):

View File

@ -0,0 +1,19 @@
# Generated by Django 5.1.5 on 2025-01-20 13:53
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wireguard', '0023_peergroup'),
]
operations = [
migrations.AlterField(
model_name='peergroup',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
),
]

View File

@ -129,12 +129,5 @@ class PeerGroup(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True) updated = models.DateTimeField(auto_now=True)
uuid = models.UUIDField(primary_key=True, editable=False) uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
def clean(self):
if self.peer.exists() and self.server_instance.exists():
raise ValidationError("Please choose either WireGuard Instances or Peers, not both.")
def save(self, *args, **kwargs):
self.clean()
super().save(*args, **kwargs)

View File

@ -19,7 +19,7 @@ from django.urls import path
from wireguard.views import view_welcome, view_wireguard_status, view_wireguard_manage_instance from wireguard.views import view_welcome, view_wireguard_status, view_wireguard_manage_instance
from wireguard_peer.views import view_wireguard_peer_list, view_wireguard_peer_manage, view_manage_ip_address from wireguard_peer.views import view_wireguard_peer_list, view_wireguard_peer_manage, view_manage_ip_address
from console.views import view_console from console.views import view_console
from user_manager.views import view_user_list, view_manage_user from user_manager.views import view_user_list, view_manage_user, view_peer_group_list, view_peer_group_manage
from accounts.views import view_create_first_user, view_login, view_logout from accounts.views import view_create_first_user, view_login, view_logout
from wireguard_tools.views import export_wireguard_configs, download_config_or_qrcode, restart_wireguard_interfaces from wireguard_tools.views import export_wireguard_configs, download_config_or_qrcode, restart_wireguard_interfaces
from api.views import wireguard_status, cron_check_updates, cron_update_peer_latest_handshake, routerfleet_get_user_token, routerfleet_authenticate_session from api.views import wireguard_status, cron_check_updates, cron_update_peer_latest_handshake, routerfleet_get_user_token, routerfleet_authenticate_session
@ -41,6 +41,8 @@ urlpatterns = [
path('console/', view_console, name='console'), path('console/', view_console, name='console'),
path('user/list/', view_user_list, name='user_list'), path('user/list/', view_user_list, name='user_list'),
path('user/manage/', view_manage_user, name='manage_user'), path('user/manage/', view_manage_user, name='manage_user'),
path('user/peer-group/list/', view_peer_group_list, name='peer_group_list'),
path('user/peer-group/manage/', view_peer_group_manage, name='peer_group_manage'),
path('tools/export_wireguard_config/', export_wireguard_configs, name='export_wireguard_configs'), path('tools/export_wireguard_config/', export_wireguard_configs, name='export_wireguard_configs'),
path('tools/download_peer_config/', download_config_or_qrcode, name='download_config_or_qrcode'), path('tools/download_peer_config/', download_config_or_qrcode, name='download_config_or_qrcode'),
path('tools/restart_wireguard/', restart_wireguard_interfaces, name='restart_wireguard_interfaces'), path('tools/restart_wireguard/', restart_wireguard_interfaces, name='restart_wireguard_interfaces'),