diff --git a/templates/user_manager/list.html b/templates/user_manager/list.html
index d1928f7..2bf04e3 100644
--- a/templates/user_manager/list.html
+++ b/templates/user_manager/list.html
@@ -21,7 +21,6 @@
{% endfor %}
-
- Add User
+ {% include "user_manager/list_buttons.html" %}
{% endblock %}
diff --git a/templates/user_manager/list_buttons.html b/templates/user_manager/list_buttons.html
new file mode 100644
index 0000000..f3d10e4
--- /dev/null
+++ b/templates/user_manager/list_buttons.html
@@ -0,0 +1,4 @@
+Add User
+List Users
+List Peer Groups
+Add Peer Group
\ No newline at end of file
diff --git a/templates/user_manager/manage_peer_group.html b/templates/user_manager/manage_peer_group.html
new file mode 100644
index 0000000..6cdd3de
--- /dev/null
+++ b/templates/user_manager/manage_peer_group.html
@@ -0,0 +1,77 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
Peers
+
Select which peers can be managed by users with this peer group.
+
+
WireGuard Instances
+
All peers in this WireGuard instance can be managed by users with this peer group, including adding or removing peers.
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block custom_page_scripts %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/user_manager/peer_group_list.html b/templates/user_manager/peer_group_list.html
new file mode 100644
index 0000000..47e250e
--- /dev/null
+++ b/templates/user_manager/peer_group_list.html
@@ -0,0 +1,37 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+
+
+ Name |
+ Peers |
+ Server Instance |
+ |
+
+
+
+ {% for peer_group in peer_group_list %}
+
+ {{ peer_group.name }} |
+
+ {% for peer in peer_group.peer.all %}
+ {{ peer }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+ |
+
+ {% for instance in peer_group.server_instance.all %}
+ {{ instance }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+ |
+
+
+ |
+
+ {% endfor %}
+
+
+
+ {% include "user_manager/list_buttons.html" %}
+
+{% endblock %}
diff --git a/user_manager/forms.py b/user_manager/forms.py
index 392b8d6..8d4c448 100644
--- a/user_manager/forms.py
+++ b/user_manager/forms.py
@@ -3,6 +3,7 @@ from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import UserAcl
from django.core.exceptions import ValidationError
+from wireguard.models import PeerGroup
class UserAclForm(UserCreationForm):
@@ -44,3 +45,34 @@ class UserAclForm(UserCreationForm):
)
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
+
diff --git a/user_manager/views.py b/user_manager/views.py
index 6eb6e1b..6e1361d 100644
--- a/user_manager/views.py
+++ b/user_manager/views.py
@@ -5,6 +5,54 @@ from .forms import UserAclForm
from django.contrib.auth.models import User
from django.contrib import messages
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
def view_user_list(request):
diff --git a/wireguard/migrations/0024_alter_peergroup_uuid.py b/wireguard/migrations/0024_alter_peergroup_uuid.py
new file mode 100644
index 0000000..c84d3a5
--- /dev/null
+++ b/wireguard/migrations/0024_alter_peergroup_uuid.py
@@ -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),
+ ),
+ ]
diff --git a/wireguard/models.py b/wireguard/models.py
index 409d556..c83cd51 100644
--- a/wireguard/models.py
+++ b/wireguard/models.py
@@ -129,12 +129,5 @@ class PeerGroup(models.Model):
created = models.DateTimeField(auto_now_add=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)
diff --git a/wireguard_webadmin/urls.py b/wireguard_webadmin/urls.py
index 375c8b9..4b3662c 100644
--- a/wireguard_webadmin/urls.py
+++ b/wireguard_webadmin/urls.py
@@ -19,7 +19,7 @@ from django.urls import path
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 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 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
@@ -41,6 +41,8 @@ urlpatterns = [
path('console/', view_console, name='console'),
path('user/list/', view_user_list, name='user_list'),
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/download_peer_config/', download_config_or_qrcode, name='download_config_or_qrcode'),
path('tools/restart_wireguard/', restart_wireguard_interfaces, name='restart_wireguard_interfaces'),