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 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 user_manager.models import UserAcl
import subprocess
@login_required
def view_console(request):
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')
if wireguard_instances.filter(pending_changes=True).exists():
pending_changes_warning = True
@ -59,14 +65,19 @@ def view_console(request):
command_output = requested_command + ': Invalid target'
bash_command = None
command_success = False
if bash_command:
try:
command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8')
command_success = True
except subprocess.CalledProcessError as e:
command_output = e.output.decode('utf-8')
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:
try:
command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8')
command_success = True
except subprocess.CalledProcessError as e:
command_output = e.output.decode('utf-8')
command_success = False
context = {'page_title': page_title, 'command_output': command_output, 'command_success': command_success, 'pending_changes_warning': pending_changes_warning}
return render(request, 'console/console.html', context)

View File

@ -8,6 +8,8 @@
<th>User Level</th>
<th>Peer Groups</th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
@ -24,6 +26,16 @@
Any
{% endif %}
</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;">
<a href="/user/manage/?uuid={{ user_acl.uuid }}" ><i class="far fa-edit"></i></a>
</td>

View File

@ -1,6 +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>
<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>
<a href="/user/peer-group/manage/" class="btn btn-primary">Add Peer Group</a>

View File

@ -12,6 +12,8 @@ class UserAclForm(forms.Form):
username = forms.CharField(max_length=150)
password1 = forms.CharField(widget=forms.PasswordInput, required=False, label="Password")
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")
peer_groups = forms.ModelMultipleChoiceField(
queryset=PeerGroup.objects.all(),
@ -27,9 +29,16 @@ class UserAclForm(forms.Form):
self.fields['username'].initial = self.instance.username
self.fields['username'].widget.attrs['readonly'] = True
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:
self.fields['password1'].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.form_method = 'post'
@ -60,6 +69,14 @@ class UserAclForm(forms.Form):
Column('peer_groups', css_class='form-group col-md-12 mb-0'),
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(
Column(
Submit('submit', 'Save', css_class='btn btn-success'),
@ -100,6 +117,8 @@ class UserAclForm(forms.Form):
password = self.cleaned_data.get('password1')
user_level = self.cleaned_data['user_level']
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:
user = self.instance
@ -115,7 +134,9 @@ class UserAclForm(forms.Form):
user_acl, created = UserAcl.objects.update_or_create(
user=user,
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'),
))
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)
updated = models.DateTimeField(auto_now=True)

View File

@ -156,6 +156,12 @@ def view_manage_user(request):
<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>
<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,19 +78,25 @@ def view_welcome(request):
@login_required
def view_wireguard_status(request):
user_acl = get_object_or_404(UserAcl, user=request.user)
page_title = 'WireGuard Status'
wireguard_instances = WireGuardInstance.objects.all().order_by('instance_id')
if wireguard_instances.filter(pending_changes=True).exists():
pending_changes_warning = True
else:
pending_changes_warning = False
bash_command = ['bash', '-c', 'wg show']
try:
command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8')
if user_acl.enable_enhanced_filter:
command_output = 'Enhanced filter is enabled. This command is not available.'
command_success = True
except subprocess.CalledProcessError as e:
command_output = e.output.decode('utf-8')
command_success = False
else:
bash_command = ['bash', '-c', 'wg show']
try:
command_output = subprocess.check_output(bash_command, stderr=subprocess.STDOUT).decode('utf-8')
command_success = True
except subprocess.CalledProcessError as e:
command_output = e.output.decode('utf-8')
command_success = False
context = {'page_title': page_title, 'command_output': command_output, 'command_success': command_success, 'pending_changes_warning': pending_changes_warning, 'wireguard_instances': wireguard_instances}
return render(request, 'wireguard/wireguard_status.html', context)