User and permission management

This commit is contained in:
Eduardo Silva 2024-02-15 11:02:22 -03:00
parent 3177eb2b8d
commit f036daf779
10 changed files with 296 additions and 10 deletions

View File

@ -3,6 +3,7 @@ from django.contrib.auth.models import User
from django.contrib import auth from django.contrib import auth
from .forms import CreateUserForm, LoginForm from .forms import CreateUserForm, LoginForm
from django.http import HttpResponse from django.http import HttpResponse
from user_manager.models import UserAcl
def view_create_first_user(request): def view_create_first_user(request):
@ -13,7 +14,8 @@ def view_create_first_user(request):
if form.is_valid(): if form.is_valid():
username = form.cleaned_data['username'] username = form.cleaned_data['username']
password = form.cleaned_data['password'] password = form.cleaned_data['password']
User.objects.create_superuser(username=username, password=password) new_user = User.objects.create_superuser(username=username, password=password)
UserAcl.objects.create(user=new_user, user_level=50)
return render(request, 'accounts/superuser_created.html') return render(request, 'accounts/superuser_created.html')
else: else:
form = CreateUserForm() form = CreateUserForm()

View File

@ -1,5 +1,25 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
this page is just a placeholder for the moment <table class="table table-striped">
<thead>
<tr>
<th>Username</th>
<th>User Level</th>
<th></th>
</tr>
</thead>
<tbody>
{% for user_acl in user_acl_list %}
<tr>
<td>{{ user_acl.user.username }}</td>
<td>{{ user_acl.get_user_level_display }}</td>
<td style="width: 1%; white-space: nowrap;">
<a href="/user/manage/?uuid={{ user_acl.uuid }}" ><i class="far fa-edit"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,97 @@
{% 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 User,Create New User" }}</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<form method="post">
{% csrf_token %}
<!-- Username -->
<div class="form-group">
<label for="{{ form.username.id_for_label }}">Username</label>
<input type="text" class="form-control" id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" placeholder="Enter Username" value="{{ form.username.value|default_if_none:'' }}" {% if form.instance.pk %}readonly{% endif %}>
</div>
<!-- Password -->
<div class="form-group">
<label for="{{ form.password1.id_for_label }}">Password</label>
<input type="password" class="form-control" id="{{ form.password1.id_for_label }}" name="{{ form.password1.html_name }}" placeholder="Password">
</div>
<!-- Retype Password -->
<div class="form-group">
<label for="{{ form.password2.id_for_label }}">Retype Password</label>
<input type="password" class="form-control" id="{{ form.password2.id_for_label }}" name="{{ form.password2.html_name }}" placeholder="Retype Password">
</div>
<!-- User Level -->
<div class="form-group">
<label for="{{ form.user_level.id_for_label }}">{{ form.user_level.label }}</label>
<select class="form-control" id="{{ form.user_level.id_for_label }}" name="{{ form.user_level.html_name }}">
{% for value, display in form.user_level.field.choices %}
<option value="{{ value }}" {% if form.user_level.value|stringformat:"s" == value|stringformat:"s" %}selected{% endif %}>{{ display }}</option>
{% endfor %}
</select>
</div>
<div>
<button type="submit" class="btn btn-primary">Submit</button>
<a href="/user/list/" class="btn btn-outline-secondary">Back</a>
{% if user_acl %}<a href='javascript:void(0)' class='btn btn-outline-danger' data-command='delete' onclick='openCommandDialog(this)'>Delete User</a>{% endif %}
</div>
</form>
</div>
<div class="col-md-8">
<h5>Debugging Analyst</h5>
<p>Access to basic system information and logs for troubleshooting. No access to modify settings or view sensitive data such as peer keys.</p>
<h5>View Only User</h5>
<p>Full view access, including peer keys and configuration files. Cannot modify any settings or configurations.</p>
<h5>Peer Manager</h5>
<p>Permissions to add, edit, and remove peers and IP addresses. Does not include access to modify WireGuard instance configurations or higher-level settings.</p>
<h5>Wireguard Manager</h5>
<p>Authority to add, edit, and remove configurations of WireGuard instances.</p>
<h5>Administrator</h5>
<p>Full access across the system. Can view and modify all settings, configurations and manage users. </p>
</div>
</div>
<div class="row">
<div class="col-md-12">
</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 '{{ user_acl.user.username }}' to remove this user.");
if (confirmation) {
var url = "?uuid={{ user_acl.uuid }}&action=delete&confirmation=" + encodeURIComponent(confirmation);
window.location.href = url;
}
}
</script>
{% endblock %}

View File

@ -1,3 +1,9 @@
from django.contrib import admin from django.contrib import admin
from user_manager.models import UserAcl
# Register your models here.
class UserAclAdmin(admin.ModelAdmin):
list_display = ('user', 'user_level', 'created', 'updated')
search_fields = ('user__username', 'user__email')
admin.site.register(UserAcl, UserAclAdmin)

46
user_manager/forms.py Normal file
View File

@ -0,0 +1,46 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import UserAcl
from django.core.exceptions import ValidationError
class UserAclForm(UserCreationForm):
user_level = forms.ChoiceField(choices=UserAcl.user_level.field.choices, required=True, label="User Level")
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('user_level',)
def __init__(self, *args, **kwargs):
self.user_id = kwargs.pop('user_id', None)
super().__init__(*args, **kwargs)
if self.instance and self.instance.pk:
self.fields['password1'].required = False
self.fields['password2'].required = False
self.fields['username'].widget.attrs['readonly'] = True
def clean_username(self):
username = self.cleaned_data.get('username')
if User.objects.filter(username=username).exclude(pk=self.user_id).exists():
raise ValidationError("A user with that username already exists.")
return username
def save(self, commit=True):
user = super().save(commit=False)
new_password = self.cleaned_data.get("password1")
if new_password:
user.set_password(new_password)
user.save()
else:
if not user.id:
user.save()
if commit:
user_acl, created = UserAcl.objects.update_or_create(
user=user,
defaults={'user_level': self.cleaned_data.get('user_level')}
)
return user

View File

@ -0,0 +1,25 @@
# Generated by Django 5.0.1 on 2024-02-14 20:54
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserAcl',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_level', models.PositiveIntegerField(choices=[(10, 'Debugging Analyst'), (20, 'View Only User'), (30, 'Peer Manager'), (40, 'Wireguard Manager'), (50, 'Administrator')], default=0)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 5.0.1 on 2024-02-14 21:23
import django.utils.timezone
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('user_manager', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='useracl',
name='id',
),
migrations.AddField(
model_name='useracl',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='useracl',
name='updated',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='useracl',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
),
]

View File

@ -1,6 +1,6 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
# Create your models here. import uuid
class UserAcl(models.Model): class UserAcl(models.Model):
@ -11,8 +11,11 @@ class UserAcl(models.Model):
(30, 'Peer Manager'), (30, 'Peer Manager'),
(40, 'Wireguard Manager'), (40, 'Wireguard Manager'),
(50, 'Administrator'), (50, 'Administrator'),
)) ))
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
def __str__(self): def __str__(self):
return self.user.username return self.user.username

View File

@ -1,9 +1,60 @@
from django.shortcuts import render
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect, get_object_or_404
from user_manager.models import UserAcl
from .forms import UserAclForm
from django.contrib.auth.models import User
from django.contrib import messages
from django.contrib.sessions.models import Session
@login_required @login_required
def view_user_list(request): def view_user_list(request):
page_title = 'User Manager' page_title = 'User Manager'
context = {'page_title': page_title} user_acl_list = UserAcl.objects.all().order_by('user__username')
return render(request, 'user_manager/list.html', context) context = {'page_title': page_title, 'user_acl_list': user_acl_list}
return render(request, 'user_manager/list.html', context)
@login_required
def view_manage_user(request):
user_acl = None
user = None
if 'uuid' in request.GET:
user_acl = get_object_or_404(UserAcl, uuid=request.GET['uuid'])
user = user_acl.user
form = UserAclForm(instance=user, initial={'user_level': user_acl.user_level}, user_id=user.id)
page_title = 'Edit User '+ user.username
if request.GET.get('action') == 'delete':
username = user.username
if request.GET.get('confirm') == user.username:
user.delete()
messages.success(request, 'User deleted|The user '+ username +' has been deleted.')
return redirect('/user/list/')
user_acl.delete()
return redirect('/user/list/')
else:
form = UserAclForm()
page_title = 'Add User'
if request.method == 'POST':
if user_acl:
form = UserAclForm(request.POST, instance=user, user_id=user.id)
else:
form = UserAclForm(request.POST)
if form.is_valid():
form.save()
if form.cleaned_data.get('password1'):
user_disconnected = False
for session in Session.objects.all():
if str(user.id) == session.get_decoded().get('_auth_user_id'):
session.delete()
if not user_disconnected:
messages.warning(request, 'User Disconnected|The user '+ user.username +' has been disconnected.')
user_disconnected = True
if user_acl:
messages.success(request, 'User updated|The user '+ form.cleaned_data['username'] +' has been updated.')
else:
messages.success(request, 'User added|The user '+ form.cleaned_data['username'] +' has been added.')
return redirect('/user/list/')
return render(request, 'user_manager/manage_user.html', {'form': form, 'page_title': page_title, 'user_acl': user_acl})

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 from user_manager.views import view_user_list, view_manage_user
from accounts.views import view_create_first_user, view_login, view_logout from accounts.views import view_create_first_user, view_login, view_logout
@ -32,6 +32,7 @@ urlpatterns = [
path('peer/manage_ip_address/', view_manage_ip_address, name='manage_ip_address'), path('peer/manage_ip_address/', view_manage_ip_address, name='manage_ip_address'),
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('server/manage/', view_wireguard_manage_instance, name='wireguard_manage_instance'), path('server/manage/', view_wireguard_manage_instance, name='wireguard_manage_instance'),
path('accounts/create_first_user/', view_create_first_user, name='create_first_user'), path('accounts/create_first_user/', view_create_first_user, name='create_first_user'),
path('accounts/login/', view_login, name='login'), path('accounts/login/', view_login, name='login'),