mirror of
https://github.com/eduardogsilva/routerfleet.git
synced 2025-08-02 01:04:26 +02:00
import user_manager and accounts from project wireguard_webadmin
This commit is contained in:
parent
757fa6a1e1
commit
c104a12df0
28 changed files with 618 additions and 4 deletions
0
accounts/__init__.py
Normal file
0
accounts/__init__.py
Normal file
3
accounts/admin.py
Normal file
3
accounts/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
accounts/apps.py
Normal file
6
accounts/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AccountsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'accounts'
|
41
accounts/forms.py
Normal file
41
accounts/forms.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
from typing import Any
|
||||||
|
from django import forms
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
|
|
||||||
|
|
||||||
|
class CreateUserForm(forms.Form):
|
||||||
|
username = forms.CharField(label='Username')
|
||||||
|
password = forms.CharField(label='Password', widget=forms.PasswordInput)
|
||||||
|
password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput) # Adicione este campo para a confirmação da senha
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
|
||||||
|
username = cleaned_data.get("username")
|
||||||
|
if username and ' ' in username:
|
||||||
|
self.add_error('username', ValidationError("Username cannot contain spaces."))
|
||||||
|
cleaned_data['username'] = username.lower()
|
||||||
|
password = cleaned_data.get("password")
|
||||||
|
password2 = cleaned_data.get("password2")
|
||||||
|
|
||||||
|
if password and password2 and password != password2:
|
||||||
|
self.add_error('password2', ValidationError("The two password fields didn't match."))
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(forms.Form):
|
||||||
|
username = forms.CharField(label='Username')
|
||||||
|
password = forms.CharField(label='Password', widget=forms.PasswordInput)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
username = cleaned_data.get("username")
|
||||||
|
password = cleaned_data.get("password")
|
||||||
|
if username and password:
|
||||||
|
user = authenticate(username=username, password=password)
|
||||||
|
if not user:
|
||||||
|
self.add_error(None, ValidationError("Invalid username or password."))
|
||||||
|
else:
|
||||||
|
self.add_error(None, ValidationError("Both fields are required."))
|
||||||
|
return cleaned_data
|
0
accounts/migrations/__init__.py
Normal file
0
accounts/migrations/__init__.py
Normal file
3
accounts/models.py
Normal file
3
accounts/models.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
3
accounts/tests.py
Normal file
3
accounts/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
42
accounts/views.py
Normal file
42
accounts/views.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from django.shortcuts import render, Http404, redirect
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib import auth
|
||||||
|
from .forms import CreateUserForm, LoginForm
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from user_manager.models import UserAcl
|
||||||
|
|
||||||
|
|
||||||
|
def view_create_first_user(request):
|
||||||
|
if User.objects.filter().all():
|
||||||
|
raise Http404('Superuser already exists')
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = CreateUserForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
username = form.cleaned_data['username']
|
||||||
|
password = form.cleaned_data['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')
|
||||||
|
else:
|
||||||
|
form = CreateUserForm()
|
||||||
|
return render(request, 'accounts/create_first_user.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
def view_login(request):
|
||||||
|
if not User.objects.filter().all():
|
||||||
|
return redirect('/accounts/create_first_user/')
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = LoginForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
username = form.cleaned_data['username']
|
||||||
|
user = User.objects.get(username=username)
|
||||||
|
auth.login(request, user)
|
||||||
|
return redirect('/')
|
||||||
|
else:
|
||||||
|
form = LoginForm()
|
||||||
|
return render(request, 'accounts/login.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
def view_logout(request):
|
||||||
|
auth.logout(request)
|
||||||
|
return render(request, 'accounts/logout.html')
|
|
@ -1,11 +1,14 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def view_dashboard(request):
|
def view_dashboard(request):
|
||||||
context = {'page_title': 'Welcome to routerfleet'}
|
context = {'page_title': 'Welcome to routerfleet'}
|
||||||
return render(request, 'dashboard/welcome.html', context=context)
|
return render(request, 'dashboard/welcome.html', context=context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def view_status(request):
|
def view_status(request):
|
||||||
context = {'page_title': 'Welcome to routerfleet'}
|
context = {'page_title': 'Welcome to routerfleet'}
|
||||||
return render(request, 'dashboard/status.html', context=context)
|
return render(request, 'dashboard/status.html', context=context)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
asgiref==3.7.2
|
asgiref==3.7.2
|
||||||
|
crispy-bootstrap4==2024.1
|
||||||
crispy-bootstrap5==2024.2
|
crispy-bootstrap5==2024.2
|
||||||
Django==5.0.3
|
Django==5.0.3
|
||||||
django-crispy-forms==2.1
|
django-crispy-forms==2.1
|
||||||
|
|
|
@ -37,7 +37,9 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'crispy_forms'
|
'crispy_forms',
|
||||||
|
'crispy_bootstrap4',
|
||||||
|
'user_manager'
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -50,6 +52,9 @@ MIDDLEWARE = [
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap4"
|
||||||
|
CRISPY_TEMPLATE_PACK = "bootstrap4"
|
||||||
|
|
||||||
ROOT_URLCONF = 'routerfleet.urls'
|
ROOT_URLCONF = 'routerfleet.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from dashboard.views import view_dashboard, view_status
|
from dashboard.views import view_dashboard, view_status
|
||||||
|
from user_manager.views import view_manage_user, view_user_list
|
||||||
|
from accounts.views import view_login, view_logout, view_create_first_user
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('', view_dashboard, name='dashboard'),
|
path('', view_dashboard, name='dashboard'),
|
||||||
path('status/', view_status, name='status')
|
path('status/', view_status, name='status'),
|
||||||
|
path('user/list/', view_user_list, name='user_list'),
|
||||||
|
path('user/manage/', view_manage_user, name='manage_user'),
|
||||||
|
path('accounts/create_first_user/', view_create_first_user, name='create_first_user'),
|
||||||
|
path('accounts/login/', view_login, name='login'),
|
||||||
|
path('accounts/logout/', view_logout, name='logout'),
|
||||||
]
|
]
|
||||||
|
|
71
templates/accounts/create_first_user.html
Normal file
71
templates/accounts/create_first_user.html
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
{% extends "base_login.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="register-box">
|
||||||
|
<div class="register-logo">
|
||||||
|
<a href="/"><b>routerfleet</b></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body register-card-body">
|
||||||
|
<p class="login-box-msg">Create new administrator</p>
|
||||||
|
|
||||||
|
<form action="/accounts/create_first_user/" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="text" class="form-control" placeholder="username" name="username">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<span class="fas fa-user"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="password" class="form-control" placeholder="Password" name="password">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<span class="fas fa-lock"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="password" class="form-control" placeholder="Retype password" name="password2">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<span class="fas fa-lock"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<button type="submit" class="btn btn-primary btn-block">Register</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.form-box -->
|
||||||
|
</div><!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.register-box -->
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.querySelector('input[name="username"]').addEventListener('input', function() {
|
||||||
|
this.value = this.value.toLowerCase().replace(/\s/g, '');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
```
|
62
templates/accounts/login.html
Normal file
62
templates/accounts/login.html
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
{% extends "base_login.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="register-box">
|
||||||
|
<div class="register-logo">
|
||||||
|
<a href="/"><b>routerfleet</b></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body register-card-body">
|
||||||
|
<form action="/accounts/login/" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="text" class="form-control" placeholder="username" name="username">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<span class="fas fa-user"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="password" class="form-control" placeholder="Password" name="password">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<span class="fas fa-lock"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<button type="submit" class="btn btn-primary btn-block">Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.form-box -->
|
||||||
|
</div><!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.register-box -->
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.querySelector('input[name="username"]').addEventListener('input', function() {
|
||||||
|
this.value = this.value.toLowerCase().replace(/\s/g, '');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
```
|
35
templates/accounts/logout.html
Normal file
35
templates/accounts/logout.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{% extends "base_login.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="register-box">
|
||||||
|
<div class="register-logo">
|
||||||
|
<a href="/"><b>routerfleet</b></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body register-card-body">
|
||||||
|
<p class="login-box-msg">You have been successfully logged out.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<a href="/accounts/login/" class="btn btn-primary btn-block">Login again</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.form-box -->
|
||||||
|
</div><!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.register-box -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
```
|
32
templates/accounts/superuser_created.html
Normal file
32
templates/accounts/superuser_created.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{% extends "base_login.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="register-box">
|
||||||
|
<div class="register-logo">
|
||||||
|
<a href="/"><b>routerfleet</b></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body register-card-body">
|
||||||
|
<p class="login-box-msg">Superuser created!</p>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<a href="/accounts/login/" class="btn btn-primary btn-block">Proceed to login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.form-box -->
|
||||||
|
</div><!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.register-box -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{% if page_title %}{{page_title}} | {% endif %}routerfleet</title>
|
<title>{% if page_title %}{{ page_title }} | {% endif %}routerfleet</title>
|
||||||
|
|
||||||
<!-- Google Font: Source Sans Pro -->
|
<!-- Google Font: Source Sans Pro -->
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
|
||||||
|
@ -93,8 +93,15 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/user/list/" class="nav-link {% if '/user/' in request.path %}active{% endif %}">
|
||||||
|
<i class="fas fa-users nav-icon"></i>
|
||||||
|
<p>
|
||||||
|
User Manager
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<!-- /.sidebar-menu -->
|
<!-- /.sidebar-menu -->
|
||||||
|
|
27
templates/user_manager/list.html
Normal file
27
templates/user_manager/list.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<a href="/user/manage/" class="btn btn-primary">Add User</a>
|
||||||
|
|
||||||
|
{% endblock %}
|
92
templates/user_manager/manage_user.html
Normal file
92
templates/user_manager/manage_user.html
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
{% 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>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>
|
||||||
|
</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 %}
|
0
user_manager/__init__.py
Normal file
0
user_manager/__init__.py
Normal file
3
user_manager/admin.py
Normal file
3
user_manager/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
user_manager/apps.py
Normal file
6
user_manager/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class UserManagerConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'user_manager'
|
45
user_manager/forms.py
Normal file
45
user_manager/forms.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
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
|
28
user_manager/migrations/0001_initial.py
Normal file
28
user_manager/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 5.0.3 on 2024-03-12 13:05
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
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=[
|
||||||
|
('user_level', models.PositiveIntegerField(choices=[(10, 'Debugging Analyst'), (20, 'View Only User'), (30, 'Peer Manager'), (40, 'Manager'), (50, 'Administrator')], default=0)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
user_manager/migrations/__init__.py
Normal file
0
user_manager/migrations/__init__.py
Normal file
21
user_manager/models.py
Normal file
21
user_manager/models.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class UserAcl(models.Model):
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
|
user_level = models.PositiveIntegerField(default=0, choices=(
|
||||||
|
(10, 'Debugging Analyst'),
|
||||||
|
(20, 'View Only User'),
|
||||||
|
(30, 'Peer Manager'),
|
||||||
|
(40, 'Manager'),
|
||||||
|
(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):
|
||||||
|
return self.user.username
|
3
user_manager/tests.py
Normal file
3
user_manager/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
68
user_manager/views.py
Normal file
68
user_manager/views.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
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
|
||||||
|
def view_user_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 = 'User Manager'
|
||||||
|
user_acl_list = UserAcl.objects.all().order_by('user__username')
|
||||||
|
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):
|
||||||
|
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
||||||
|
return render(request, 'access_denied.html', {'page_title': 'Access Denied'})
|
||||||
|
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('confirmation') == username:
|
||||||
|
user.delete()
|
||||||
|
messages.success(request, 'User deleted|The user ' + username + ' has been deleted.')
|
||||||
|
return redirect('/user/list/')
|
||||||
|
|
||||||
|
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
|
||||||
|
if user:
|
||||||
|
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})
|
Loading…
Add table
Add a link
Reference in a new issue