User toggle for webconsole and enhanced filter

This commit is contained in:
Eduardo Silva 2025-01-21 13:22:18 -03:00
parent 64cd174639
commit 10e456b202
8 changed files with 98 additions and 19 deletions

View File

@ -1,13 +1,19 @@
from wireguard.models import WireGuardInstance from wireguard.models import WireGuardInstance
from wgwadmlibrary.tools import is_valid_ip_or_hostname from wgwadmlibrary.tools import is_valid_ip_or_hostname
from django.shortcuts import render from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from user_manager.models import UserAcl
import subprocess import subprocess
@login_required @login_required
def view_console(request): def view_console(request):
page_title = 'Console' page_title = 'Console'
user_acl = get_object_or_404(UserAcl, user=request.user)
if not user_acl.enable_console:
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
wireguard_instances = WireGuardInstance.objects.all().order_by('instance_id') wireguard_instances = WireGuardInstance.objects.all().order_by('instance_id')
if wireguard_instances.filter(pending_changes=True).exists(): if wireguard_instances.filter(pending_changes=True).exists():
pending_changes_warning = True pending_changes_warning = True
@ -60,6 +66,11 @@ def view_console(request):
bash_command = None bash_command = None
command_success = False command_success = False
if user_acl.enable_enhanced_filter and requested_command == 'wgshow':
command_output = 'Enhanced filter is enabled. This command is not available.'
bash_command = None
command_success = False
else:
if bash_command: if bash_command:
try: try:
command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8') command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8')

View File

@ -8,6 +8,8 @@
<th>User Level</th> <th>User Level</th>
<th>Peer Groups</th> <th>Peer Groups</th>
<th></th> <th></th>
<th></th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -24,6 +26,16 @@
Any Any
{% endif %} {% endif %}
</td> </td>
<td style="width: 1%; white-space: nowrap;">
{% if user_acl.enable_console %}
<i class="fas fa-terminal" title="Console Enabled"></i>
{% endif %}
</td>
<td style="width: 1%; white-space: nowrap;">
{% if user_acl.enable_enhanced_filter %}
<i class="fas fa-eye-slash" title="Enhanced Filter Enabled"></i>
{% endif %}
</td>
<td style="width: 1%; white-space: nowrap;"> <td style="width: 1%; white-space: nowrap;">
<a href="/user/manage/?uuid={{ user_acl.uuid }}" ><i class="far fa-edit"></i></a> <a href="/user/manage/?uuid={{ user_acl.uuid }}" ><i class="far fa-edit"></i></a>
</td> </td>

View File

@ -2,5 +2,3 @@
<a href="/user/list/" class="btn {% if request.path == '/user/list/' %}btn-outline-primary{% else %}btn-primary{% endif %}">List Users</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/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> <a href="/user/peer-group/manage/" class="btn btn-primary">Add Peer Group</a>
<br><br><br><h5>Warning:</h5>
<p>The limitation of Peer Groups for users is implemented. However, in some places, information about other peers may still leak, such as in Console -> wg show or through the endpoint /api/wireguard_status/. In the next version, we will add a fix for this.</p>

View File

@ -12,6 +12,8 @@ class UserAclForm(forms.Form):
username = forms.CharField(max_length=150) username = forms.CharField(max_length=150)
password1 = forms.CharField(widget=forms.PasswordInput, required=False, label="Password") password1 = forms.CharField(widget=forms.PasswordInput, required=False, label="Password")
password2 = forms.CharField(widget=forms.PasswordInput, required=False, label="Password confirmation") password2 = forms.CharField(widget=forms.PasswordInput, required=False, label="Password confirmation")
enable_console = forms.BooleanField(required=False, label="Enable Console")
enable_enhanced_filter = forms.BooleanField(required=False, label="Enable Enhanced Filter")
user_level = forms.ChoiceField(choices=UserAcl.user_level.field.choices, required=True, label="User Level") user_level = forms.ChoiceField(choices=UserAcl.user_level.field.choices, required=True, label="User Level")
peer_groups = forms.ModelMultipleChoiceField( peer_groups = forms.ModelMultipleChoiceField(
queryset=PeerGroup.objects.all(), queryset=PeerGroup.objects.all(),
@ -27,9 +29,16 @@ class UserAclForm(forms.Form):
self.fields['username'].initial = self.instance.username self.fields['username'].initial = self.instance.username
self.fields['username'].widget.attrs['readonly'] = True self.fields['username'].widget.attrs['readonly'] = True
self.fields['peer_groups'].initial = self.instance.useracl.peer_groups.all() self.fields['peer_groups'].initial = self.instance.useracl.peer_groups.all()
self.fields['enable_console'].initial = self.instance.useracl.enable_console
self.fields['enable_enhanced_filter'].initial = self.instance.useracl.enable_enhanced_filter
else: else:
self.fields['password1'].required = True self.fields['password1'].required = True
self.fields['password2'].required = True self.fields['password2'].required = True
self.fields['enable_console'].initial = True
self.fields['enable_enhanced_filter'].initial = False
self.fields['enable_console'].label = "Console"
self.fields['enable_enhanced_filter'].label = "Enhanced Filter"
self.helper = FormHelper() self.helper = FormHelper()
self.helper.form_method = 'post' self.helper.form_method = 'post'
@ -60,6 +69,14 @@ class UserAclForm(forms.Form):
Column('peer_groups', css_class='form-group col-md-12 mb-0'), Column('peer_groups', css_class='form-group col-md-12 mb-0'),
css_class='form-row' css_class='form-row'
), ),
Row(
Column('enable_console', css_class='form-group col-md-12 mb-0'),
css_class='form-row'
),
Row(
Column('enable_enhanced_filter', css_class='form-group col-md-12 mb-0'),
css_class='form-row'
),
Row( Row(
Column( Column(
Submit('submit', 'Save', css_class='btn btn-success'), Submit('submit', 'Save', css_class='btn btn-success'),
@ -100,6 +117,8 @@ class UserAclForm(forms.Form):
password = self.cleaned_data.get('password1') password = self.cleaned_data.get('password1')
user_level = self.cleaned_data['user_level'] user_level = self.cleaned_data['user_level']
peer_groups = self.cleaned_data.get('peer_groups', []) peer_groups = self.cleaned_data.get('peer_groups', [])
enable_console = self.cleaned_data.get('enable_console', False)
enable_enhanced_filter = self.cleaned_data.get('enable_enhanced_filter', False)
if self.instance: if self.instance:
user = self.instance user = self.instance
@ -115,7 +134,9 @@ class UserAclForm(forms.Form):
user_acl, created = UserAcl.objects.update_or_create( user_acl, created = UserAcl.objects.update_or_create(
user=user, user=user,
defaults={ defaults={
'user_level': user_level 'user_level': user_level,
'enable_console': enable_console,
'enable_enhanced_filter': enable_enhanced_filter
} }
) )

View File

@ -0,0 +1,23 @@
# Generated by Django 5.1.5 on 2025-01-21 15:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('user_manager', '0004_useracl_peer_groups'),
]
operations = [
migrations.AddField(
model_name='useracl',
name='enable_console',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='useracl',
name='enable_enhanced_filter',
field=models.BooleanField(default=False),
),
]

View File

@ -14,6 +14,8 @@ class UserAcl(models.Model):
(50, 'Administrator'), (50, 'Administrator'),
)) ))
peer_groups = models.ManyToManyField(PeerGroup, blank=True) peer_groups = models.ManyToManyField(PeerGroup, blank=True)
enable_console = models.BooleanField(default=True)
enable_enhanced_filter = models.BooleanField(default=False)
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)

View File

@ -156,6 +156,12 @@ def view_manage_user(request):
<h4>Peer Groups</h4> <h4>Peer Groups</h4>
<p>Select which peer groups this user can access. If no peer groups are selected, the user will have access to all peers.</p> <p>Select which peer groups this user can access. If no peer groups are selected, the user will have access to all peers.</p>
<h4>Console</h4>
<p>Enable or disable web console access for this user.</p>
<h4>Enhanced Filter</h4>
<p>This option filters the API status response to include only peers that the user has access to. Depending on the size of your environment, enabling this option may impact performance. To mitigate this, consider increasing the "Web Refresh Interval" to reduce the number of requests.</p>
''' '''
} }

View File

@ -78,12 +78,18 @@ def view_welcome(request):
@login_required @login_required
def view_wireguard_status(request): def view_wireguard_status(request):
user_acl = get_object_or_404(UserAcl, user=request.user)
page_title = 'WireGuard Status' page_title = 'WireGuard Status'
wireguard_instances = WireGuardInstance.objects.all().order_by('instance_id') wireguard_instances = WireGuardInstance.objects.all().order_by('instance_id')
if wireguard_instances.filter(pending_changes=True).exists(): if wireguard_instances.filter(pending_changes=True).exists():
pending_changes_warning = True pending_changes_warning = True
else: else:
pending_changes_warning = False pending_changes_warning = False
if user_acl.enable_enhanced_filter:
command_output = 'Enhanced filter is enabled. This command is not available.'
command_success = True
else:
bash_command = ['bash', '-c', 'wg show'] bash_command = ['bash', '-c', 'wg show']
try: try:
command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8') command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8')