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_NAME=wallet
DB_USER=postgres DB_USER=postgres
DB_PASSWORD=BatManu#171017 DB_PASSWORD=BatManu#171017
DB_HOST=10.211.114.101 DB_HOST=10.211.114.101
DB_PORT=5432 DB_PORT=5432
MASTER_PASSWORD=pipperepettenuse
MQTT_HOST=10.211.114.214 MQTT_HOST=10.211.114.214
MQTT_PORT=1883 MQTT_PORT=1883
MQTT_KEEPALIVE=60 MQTT_KEEPALIVE=60
@@ -11,3 +13,6 @@ MQTT_USER=alex
MQTT_PASSWORD=batt1l0 MQTT_PASSWORD=batt1l0
MQTT_DS_TOPIC="$CONTROL/dynamic-security/v1" MQTT_DS_TOPIC="$CONTROL/dynamic-security/v1"
MQTT_DS_RESP_TOPIC="$CONTROL/dynamic-security/v1/response" 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 %} {% block content %}
<h1 class="mb-4">{% trans "Sign In" %}</h1> <h1 class="mb-4">{% trans "Sign In" %}</h1>
<p class="mb-4"> <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 <a
href="{% url 'account_signup' %}?next={% url 'profile-onboarding' %}" href="{% url 'account_signup' %}?next={% url 'profile-onboarding' %}"
class="text-decoration-none" class="text-decoration-none"

View File

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

View File

@@ -1,8 +1,8 @@
{% load static %} {% 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> <div>
<a class="d-flex align-items-center gap-2 text-decoration-none text-white" href="/"> <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> <span class="text-lg fw-bold">MQTT User Management</span>
</a> </a>
</div> </div>
@@ -14,13 +14,17 @@
</li> </li>
<li class="nav-item dropdown"> <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"> <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 }} {{ request.user.profile.name }}
</a> </a>
<ul class="dropdown-menu dropdown-menu-end shadow-lg" style="min-width: 200px;"> <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' %}">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-edit' %}">Edit Profile</a></li>
<li><a class="dropdown-item" href="{% url 'profile-settings' %}">Settings</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><hr class="dropdown-divider" /></li>
<li><a class="dropdown-item" href="{% url 'account_logout' %}">Log Out</a></li> <li><a class="dropdown-item" href="{% url 'account_logout' %}">Log Out</a></li>
</ul> </ul>

View File

@@ -3,7 +3,7 @@
<div> <div>
{% for message in messages %} {% for message in messages %}
<div <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" style="width: 100%; max-width: 576px"
> >
<div <div

View File

@@ -1,5 +1,5 @@
{% extends 'base.html' %} {% block layout %} {% extends 'base.html' %} {% block layout %}
<div class="container"> <div class="container" style="margin-top: 20px;">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-12 col-md-8 col-lg-6"> <div class="col-12 col-md-8 col-lg-6">
<div class="bg-white rounded-3 p-4 p-md-5 shadow-lg"> <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 %} {% 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> <h1>Add User</h1>
<form method="post" hx-post="/create_user" hx-target="#content"> <form method="post" hx-post="/create_user" hx-target="#content">
{% csrf_token %} {% csrf_token %}

View File

@@ -1,16 +1,5 @@
{% extends 'layouts/blank.html' %} {% block content %} {% 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> <h1>Add User</h1>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}

View File

@@ -1,20 +1,12 @@
{% extends 'layouts/blank.html' %} {% block content %} {% 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> <h1>List Mosquitto Users</h1>
{% if ase_adm_group %}
<a href="{% url 'create_user' %}"> <a href="{% url 'create_user' %}">
<button class="btn btn-primary mb-3">Add User</button> <button class="btn btn-primary mb-3">Add User</button>
</a> </a>
{% endif %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@@ -29,20 +21,35 @@
{% for user in users %} {% for user in users %}
<tr> <tr>
<td>{{user.site}}</td> <td>{{user.site}}</td>
<td> <td>{% if ase_adm_group %}
<a href="{% url 'edit_user' user.slug %}">{{user.username}}</a <a href="{% url 'edit_user' user.slug %}">
> {% endif %}
{{user.username}}</a>
</td> </td>
<td>{{user.client_id}}</td> <td>{{user.client_id}}</td>
<td>{{user.topic}}</td> <td>{{user.topic}}</td>
<td id="user-status"> <td id="user-status">
{% if user.status == "enabled" %} {% if user.status == "enabled" %}
<a href="{% url 'disable_user' user.slug %}"> <a
<button class="btn btn-danger btn-sm">Disable</button> {% 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> </a>
{% else %} {% else %}
<a href="{% url 'enable_user' user.slug %}"> <a
<button class="btn btn-warning btn-sm">Enable</button> {% 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> </a>
{% endif %} {% endif %}
</td> </td>

View File

@@ -157,3 +157,13 @@ ACCOUNT_SIGNUP_REDIRECT_URL = "{% url 'account_signup' %}?next={% url 'profile-o
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
ACCOUNT_AUTHENTICATION_METHOD = 'email' 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 from django.apps import AppConfig
class WalletApiConfig(AppConfig): class WalletApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'wallet_api' 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 = [ urlpatterns = [
path('', views.home_view, name="home"), 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('get/', views.get_password_api, name='get_password'),
path('list/', views.list_sites_api, name='list_sites'), path('list/', views.list_sites_api, name='list_sites'),
path('publish', views.publish_message, name='publish'), 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.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages from django.contrib import messages
from django.http import JsonResponse, HttpResponse from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required, permission_required
import json import json
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
@@ -16,10 +16,11 @@ from .mqtt_service import MosquittoDynamicSecurity
config = dotenv_values(".env") config = dotenv_values(".env")
authenticate(config['MASTER_PASSWORD']) # authenticate(config['MASTER_PASSWORD'])
def home_view(request): 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 @csrf_exempt
def publish_message(request): def publish_message(request):
@@ -33,11 +34,14 @@ def publish_message(request):
protocol=mqtt.MQTTv5) protocol=mqtt.MQTTv5)
return JsonResponse({'request_data': request_data}) return JsonResponse({'request_data': request_data})
@login_required
def list_users(request): def list_users(request):
users = PasswordEntry.objects.all() 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): def create_user(request):
if request.user.groups.filter(name='ase_admin').exists():
if request.method == 'POST': if request.method == 'POST':
site = request.POST.get('site') site = request.POST.get('site')
username = request.POST.get('username') username = request.POST.get('username')
@@ -82,14 +86,23 @@ def create_user(request):
# Se la richiesta non è POST, mostra il form di creazione utente # Se la richiesta non è POST, mostra il form di creazione utente
return render(request, 'wallet_api/create_user.html') 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): def edit_user(request, slug):
if request.user.groups.filter(name='ase_admin').exists():
user = PasswordEntry.objects.filter(slug=slug).first() user = PasswordEntry.objects.filter(slug=slug).first()
return render(request, 'wallet_api/edit_user.html', {'user': user}) 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): def disable_user(request, slug):
if request.user.groups.filter(name='ase_admin').exists():
user = PasswordEntry.objects.filter(slug=slug).values('id','username') user = PasswordEntry.objects.filter(slug=slug).values('id','username')
print(user.first())
# Comando per creare un utente # Comando per creare un utente
command = { command = {
"commands": "commands":
@@ -106,9 +119,7 @@ def disable_user(request, slug):
response = mqtt_service.send_command(command) response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]: if "error" not in response["responses"][0]:
PasswordEntry.objects.update( PasswordEntry.objects.filter(id=int(user.first()["id"])).update(status='disabled')
status='disabled'
)
messages.success(request, 'Utente disabilitato con successo!') # Messaggio di successo messages.success(request, 'Utente disabilitato con successo!') # Messaggio di successo
if request.htmx: if request.htmx:
@@ -117,10 +128,14 @@ def disable_user(request, slug):
else: else:
messages.warning(request, f'Errore durante la disabilitazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore 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 return redirect('list_users') # Reindirizza alla lista degli utenti
else:
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): def enable_user(request, slug):
if request.user.groups.filter(name='ase_admin').exists():
user = PasswordEntry.objects.filter(slug=slug).values('id','username') user = PasswordEntry.objects.filter(slug=slug).values('id','username')
print(user.first())
# Comando per creare un utente # Comando per creare un utente
command = { command = {
"commands": "commands":
@@ -137,9 +152,7 @@ def enable_user(request, slug):
response = mqtt_service.send_command(command) response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]: if "error" not in response["responses"][0]:
PasswordEntry.objects.update( PasswordEntry.objects.filter(id=int(user.first()["id"])).update(status='enabled')
status='enabled'
)
messages.success(request, 'Utente abilitato con successo!') # Messaggio di successo messages.success(request, 'Utente abilitato con successo!') # Messaggio di successo
@@ -147,6 +160,9 @@ def enable_user(request, slug):
else: else:
messages.warning(request, f'Errore durante la abilitazione dell\'utente: {response["responses"][0]["error"]}') # Messaggio di errore 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 return redirect('list_users') # Reindirizza alla lista degli utenti
else:
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): def view_role(request, role):
return render(request, 'wallet_api/role_info.html', {'role': role }) return render(request, 'wallet_api/role_info.html', {'role': role })
@@ -174,6 +190,7 @@ def get_password_api(request):
}) })
except PasswordEntry.DoesNotExist: except PasswordEntry.DoesNotExist:
return JsonResponse({"error": "Sito non trovato"}, status=404) return JsonResponse({"error": "Sito non trovato"}, status=404)
return JsonResponse({"error": "Richiesta non valida"}, status=400)
@csrf_exempt @csrf_exempt
def list_sites_api(request): def list_sites_api(request):
@@ -186,29 +203,4 @@ def list_sites_api(request):
sites = PasswordEntry.objects.values_list('site', flat=True) sites = PasswordEntry.objects.values_list('site', flat=True)
return JsonResponse({"sites": list(sites)}) return JsonResponse({"sites": list(sites)})
return JsonResponse({"error": "Richiesta non valida"}, status=400)
@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"})