set master pwd al primo lancio - ruoli

This commit is contained in:
2025-02-02 16:35:34 +01:00
parent e3ae8300f1
commit 2d172a3620
16 changed files with 174 additions and 168 deletions

7
.env
View File

@@ -1,9 +1,11 @@
MASTER_PASSWORD=pipperepettenuse
DB_NAME=wallet
DB_USER=postgres
DB_PASSWORD=BatManu#171017
DB_HOST=10.211.114.101
DB_PORT=5432
MASTER_PASSWORD=pipperepettenuse
MQTT_HOST=10.211.114.214
MQTT_PORT=1883
MQTT_KEEPALIVE=60
@@ -11,3 +13,6 @@ MQTT_USER=alex
MQTT_PASSWORD=batt1l0
MQTT_DS_TOPIC="$CONTROL/dynamic-security/v1"
MQTT_DS_RESP_TOPIC="$CONTROL/dynamic-security/v1/response"
GMAIL_USER=alessandro.battilani@gmail.com
GMAIL_PASSWORD=batmanu#171017

BIN
avatars/ape1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
avatars/ape2_hSVw8me.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View File

@@ -7,7 +7,7 @@
{% block content %}
<h1 class="mb-4">{% trans "Sign In" %}</h1>
<p class="mb-4">
If you have not created an account yet, then please
{% trans "If you have not created an account yet, then please" %}
<a
href="{% url 'account_signup' %}?next={% url 'profile-onboarding' %}"
class="text-decoration-none"

View File

@@ -1,7 +1,7 @@
{% load static %}
{% load django_htmx %}
<!DOCTYPE html>
<html lang="en">
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -23,10 +23,11 @@
</style>
</head>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
{% include 'includes/messages.html' %}
{% include 'includes/header.html' %}
{% include 'includes/messages.html' %}
{% block layout %}
{% endblock %}

View File

@@ -1,8 +1,8 @@
{% load static %}
<header class="d-flex align-items-center justify-content-between bg-dark text-white py-3 px-4 sticky-top z-40">
<header class="d-flex align-items-center justify-content-between bg-secondary text-white py-1 px-4 sticky-top z-40">
<div>
<a class="d-flex align-items-center gap-2 text-decoration-none text-white" href="/">
<img class="h-6" src="{% static 'images/ase-logo.png' %}" alt="ASE" height="40"/>
<img class="h-6" src="{% static 'images/ase-logo.png' %}" alt="ASE" height="30"/>
<span class="text-lg fw-bold">MQTT User Management</span>
</a>
</div>
@@ -14,13 +14,17 @@
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle d-flex align-items-center gap-2 text-white" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<img class="h-8 w-8 rounded-circle object-cover" src="{{ request.user.profile.avatar }}" alt="Avatar" height="40"/>
<img class="h-8 w-8 rounded-circle object-cover" src="{{ request.user.profile.avatar }}" alt="Avatar" height="30"/>
{{ request.user.profile.name }}
</a>
<ul class="dropdown-menu dropdown-menu-end shadow-lg" style="min-width: 200px;">
<li><a class="dropdown-item" href="{% url 'profile' %}">My Profile</a></li>
<li><a class="dropdown-item" href="{% url 'profile-edit' %}">Edit Profile</a></li>
<li><a class="dropdown-item" href="{% url 'profile-settings' %}">Settings</a></li>
{% if request.user.is_superuser %}
<li><hr class="dropdown-divider" /></li>
<li><a class="dropdown-item" href="{% url 'admin:index' %}">Site Admin</a></li>
{% endif %}
<li><hr class="dropdown-divider" /></li>
<li><a class="dropdown-item" href="{% url 'account_logout' %}">Log Out</a></li>
</ul>

View File

@@ -3,7 +3,7 @@
<div>
{% for message in messages %}
<div
class="position-fixed top-0 start-50 translate-middle-x mt-3 z-50"
class="position-fixed top-10 start-50 translate-middle-x mt-3 z-30"
style="width: 100%; max-width: 576px"
>
<div

View File

@@ -1,5 +1,5 @@
{% extends 'base.html' %} {% block layout %}
<div class="container">
<div class="container" style="margin-top: 20px;">
<div class="row justify-content-center">
<div class="col-12 col-md-8 col-lg-6">
<div class="bg-white rounded-3 p-4 p-md-5 shadow-lg">

View File

@@ -1,18 +1,5 @@
{% extends 'layouts/blank.html' %} {% block content %}
<!-- Mostra i messaggi di errore o successo -->
{% if messages %}
<div>
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<!-- Form per la creazione dell'utente -->
<h1>Add User</h1>
<form method="post" hx-post="/create_user" hx-target="#content">
{% csrf_token %}

View File

@@ -1,16 +1,5 @@
{% extends 'layouts/blank.html' %} {% block content %}
<!-- Mostra i messaggi di errore o successo -->
{% if messages %}
<div>
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<h1>Add User</h1>
<form method="post">
{% csrf_token %}

View File

@@ -1,20 +1,12 @@
{% extends 'layouts/blank.html' %} {% block content %}
<!-- Mostra i messaggi di errore o successo -->
{% if messages %}
<div>
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<h1>List Mosquitto Users</h1>
<a href="{% url 'create_user' %}">
<button class="btn btn-primary mb-3">Add User</button>
</a>
{% if ase_adm_group %}
<a href="{% url 'create_user' %}">
<button class="btn btn-primary mb-3">Add User</button>
</a>
{% endif %}
<table class="table table-striped">
<thead>
<tr>
@@ -29,20 +21,35 @@
{% for user in users %}
<tr>
<td>{{user.site}}</td>
<td>
<a href="{% url 'edit_user' user.slug %}">{{user.username}}</a
>
<td>{% if ase_adm_group %}
<a href="{% url 'edit_user' user.slug %}">
{% endif %}
{{user.username}}</a>
</td>
<td>{{user.client_id}}</td>
<td>{{user.topic}}</td>
<td id="user-status">
{% if user.status == "enabled" %}
<a href="{% url 'disable_user' user.slug %}">
<button class="btn btn-danger btn-sm">Disable</button>
<a
{% if ase_adm_group %}
href="{% url 'disable_user' user.slug %}"
{% endif %}>
<button class="btn btn-danger btn-sm"
{% if not ase_adm_group %}
disabled
{% endif %}
>Disable</button>
</a>
{% else %}
<a href="{% url 'enable_user' user.slug %}">
<button class="btn btn-warning btn-sm">Enable</button>
<a
{% if ase_adm_group %}
href="{% url 'enable_user' user.slug %}"
{% endif %}>
<button class="btn btn-warning btn-sm"
{% if not ase_adm_group %}
disabled
{% endif %}
>Enable</button>
</a>
{% endif %}
</td>

View File

@@ -156,4 +156,14 @@ ACCOUNT_SIGNUP_REDIRECT_URL = "{% url 'account_signup' %}?next={% url 'profile-o
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_REQUIRED = True
'''
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'tuo_indirizzo@gmail.com'
EMAIL_HOST_PASSWORD = 'tua_password_gmail'
DEFAULT_FROM_EMAIL = 'tuo_indirizzo@gmail.com'
'''

View File

@@ -1,6 +1,8 @@
from django.apps import AppConfig
class WalletApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'wallet_api'
def ready(self):
import wallet_api.signals

10
wallet_api/signals.py Normal file
View File

@@ -0,0 +1,10 @@
from django.db.models.signals import post_migrate
from django.dispatch import receiver
from dotenv import dotenv_values
from .utils import authenticate
@receiver(post_migrate)
def init_master_passwored(sender, **kwargs):
if sender.name == 'wallet_api':
config = dotenv_values(".env")
authenticate(config['MASTER_PASSWORD'])

View File

@@ -3,7 +3,6 @@ from . import views
urlpatterns = [
path('', views.home_view, name="home"),
path('add/', views.add_password_api, name='add_user'),
path('get/', views.get_password_api, name='get_password'),
path('list/', views.list_sites_api, name='list_sites'),
path('publish', views.publish_message, name='publish'),

View File

@@ -1,8 +1,8 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required, permission_required
import json
import paho.mqtt.client as mqtt
@@ -16,10 +16,11 @@ from .mqtt_service import MosquittoDynamicSecurity
config = dotenv_values(".env")
authenticate(config['MASTER_PASSWORD'])
# authenticate(config['MASTER_PASSWORD'])
def home_view(request):
return render(request, 'home.html')
return redirect('list_users') # Reindirizza alla lista degli utenti
#return render(request, 'home.html')
@csrf_exempt
def publish_message(request):
@@ -33,26 +34,82 @@ def publish_message(request):
protocol=mqtt.MQTTv5)
return JsonResponse({'request_data': request_data})
@login_required
def list_users(request):
users = PasswordEntry.objects.all()
return render(request, 'wallet_api/list_users.html', {'users': users})
return render(request, 'wallet_api/list_users.html', {'users': users, 'ase_adm_group': request.user.groups.filter(name='ase_admin').exists()})
@login_required
def create_user(request):
if request.method == 'POST':
site = request.POST.get('site')
username = request.POST.get('username')
client_id = request.POST.get('clientId')
topic = request.POST.get('topic')
password = request.POST.get('password')
if request.user.groups.filter(name='ase_admin').exists():
if request.method == 'POST':
site = request.POST.get('site')
username = request.POST.get('username')
client_id = request.POST.get('clientId')
topic = request.POST.get('topic')
password = request.POST.get('password')
# Comando per creare un utente
command = {
"commands":
[
{
"command": "createClient",
"username": username,
"password": password
}
]
}
# Invia il comando a Mosquitto
mqtt_service = MosquittoDynamicSecurity()
response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]:
key = derive_key(config['MASTER_PASSWORD'])
encrypted_password = encrypt_password(password, key)
PasswordEntry.objects.create(
site=site,
username=username,
password=encrypted_password,
client_id=client_id,
topic=topic,
status='enabled'
)
messages.success(request, 'Utente creato con successo!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
else:
messages.warning(request, f'Errore durante la creazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore
return render(request, 'wallet_api/create_user.html', {'response': response}) # Rimani sulla stessa vista
# Se la richiesta non è POST, mostra il form di creazione utente
return render(request, 'wallet_api/create_user.html')
else:
messages.success(request, 'Non hai i permessi per creare utenti MQTT!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
@login_required
def edit_user(request, slug):
if request.user.groups.filter(name='ase_admin').exists():
user = PasswordEntry.objects.filter(slug=slug).first()
return render(request, 'wallet_api/edit_user.html', {'user': user})
else:
messages.success(request, 'Non hai i permessi per creare utenti MQTT!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
@login_required
def disable_user(request, slug):
if request.user.groups.filter(name='ase_admin').exists():
user = PasswordEntry.objects.filter(slug=slug).values('id','username')
# Comando per creare un utente
command = {
"commands":
[
{
"command": "createClient",
"username": username,
"password": password
"command": "disableClient",
"username": user.first()["username"]
}
]
}
@@ -62,90 +119,49 @@ def create_user(request):
response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]:
key = derive_key(config['MASTER_PASSWORD'])
encrypted_password = encrypt_password(password, key)
PasswordEntry.objects.filter(id=int(user.first()["id"])).update(status='disabled')
PasswordEntry.objects.create(
site=site,
username=username,
password=encrypted_password,
client_id=client_id,
topic=topic,
status='enabled'
)
messages.success(request, 'Utente creato con successo!') # Messaggio di successo
messages.success(request, 'Utente disabilitato con successo!') # Messaggio di successo
if request.htmx:
return render(request, 'partials/enable_user.html')
return redirect('list_users') # Reindirizza alla lista degli utenti
else:
messages.warning(request, f'Errore durante la creazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore
return render(request, 'wallet_api/create_user.html', {'response': response}) # Rimani sulla stessa vista
# Se la richiesta non è POST, mostra il form di creazione utente
return render(request, 'wallet_api/create_user.html')
def edit_user(request, slug):
user = PasswordEntry.objects.filter(slug=slug).first()
return render(request, 'wallet_api/edit_user.html', {'user': user})
def disable_user(request, slug):
user = PasswordEntry.objects.filter(slug=slug).values('id','username')
print(user.first())
# Comando per creare un utente
command = {
"commands":
[
{
"command": "disableClient",
"username": user.first()["username"]
}
]
}
# Invia il comando a Mosquitto
mqtt_service = MosquittoDynamicSecurity()
response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]:
PasswordEntry.objects.update(
status='disabled'
)
messages.success(request, 'Utente disabilitato con successo!') # Messaggio di successo
if request.htmx:
return render(request, 'partials/enable_user.html')
return redirect('list_users') # Reindirizza alla lista degli utenti
messages.warning(request, f'Errore durante la disabilitazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore
return redirect('list_users') # Reindirizza alla lista degli utenti
else:
messages.warning(request, f'Errore durante la disabilitazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore
messages.success(request, 'Non hai i permessi per disabilitare utenti MQTT!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
@login_required
def enable_user(request, slug):
user = PasswordEntry.objects.filter(slug=slug).values('id','username')
print(user.first())
# Comando per creare un utente
command = {
"commands":
[
{
"command": "enableClient",
"username": user.first()["username"]
}
]
}
if request.user.groups.filter(name='ase_admin').exists():
user = PasswordEntry.objects.filter(slug=slug).values('id','username')
# Comando per creare un utente
command = {
"commands":
[
{
"command": "enableClient",
"username": user.first()["username"]
}
]
}
# Invia il comando a Mosquitto
mqtt_service = MosquittoDynamicSecurity()
response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]:
# Invia il comando a Mosquitto
mqtt_service = MosquittoDynamicSecurity()
response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]:
PasswordEntry.objects.update(
status='enabled'
)
PasswordEntry.objects.filter(id=int(user.first()["id"])).update(status='enabled')
messages.success(request, 'Utente abilitato con successo!') # Messaggio di successo
messages.success(request, 'Utente abilitato con successo!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
return redirect('list_users') # Reindirizza alla lista degli utenti
else:
messages.warning(request, f'Errore durante la abilitazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore
return redirect('list_users') # Reindirizza alla lista degli utenti
else:
messages.warning(request, f'Errore durante la abilitazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore
messages.success(request, 'Non hai i permessi per disabilitare utenti MQTT!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
def view_role(request, role):
@@ -174,6 +190,7 @@ def get_password_api(request):
})
except PasswordEntry.DoesNotExist:
return JsonResponse({"error": "Sito non trovato"}, status=404)
return JsonResponse({"error": "Richiesta non valida"}, status=400)
@csrf_exempt
def list_sites_api(request):
@@ -186,29 +203,4 @@ def list_sites_api(request):
sites = PasswordEntry.objects.values_list('site', flat=True)
return JsonResponse({"sites": list(sites)})
@csrf_exempt
def add_password_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
site = data.get('site')
username = data.get('username')
password = data.get('password')
client_id = data.get('client_id')
topic = data.get('topic')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
key = derive_key(master_password)
encrypted_password = encrypt_password(password, key)
PasswordEntry.objects.create(
site=site,
username=username,
password=encrypted_password,
client_id=client_id,
topic=topic
)
return JsonResponse({"message": "Password aggiunta con successo"})
return JsonResponse({"error": "Richiesta non valida"}, status=400)