fix + test api with resl client

This commit is contained in:
2025-02-06 15:10:51 +01:00
parent 2d172a3620
commit 861a0e58e5
20 changed files with 376 additions and 254 deletions

5
.env
View File

@@ -13,6 +13,7 @@ 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"
MQTT_PWDX=AsE
GMAIL_USER=alessandro.battilani@gmail.com GMAIL_USER=
GMAIL_PASSWORD=batmanu#171017 GMAIL_PASSWORD=

16
.vscode/settings.json vendored
View File

@@ -1,4 +1,18 @@
{ {
"taipyStudio.config.mainPythonFile": "manage.py", "taipyStudio.config.mainPythonFile": "manage.py",
"taipyStudio.gUI.elementsFilePaths": [] "taipyStudio.gUI.elementsFilePaths": [],
"rest-client.environmentVariables": {
"$shared": {
"version": "v1",
"prodToken": "foo",
"nonProdToken": "bar"
},
"test": {
"version": "v2",
"host": "localhost"
},
"production": {
"host": "aaaaa",
}
}
} }

16
Test_API_wallet.http Normal file
View File

@@ -0,0 +1,16 @@
POST http://{{host}}:8000/get/ HTTP/1.1
Content-Type: application/json
{
"master_password": "pipperepettenuse",
"site": "enertec_ase_site51"
}
####
POST http://{{host}}:8000/list/ HTTP/1.1
Content-Type: application/json
{
"master_password": "pipperepettenuse"
}

View File

@@ -1,54 +1,86 @@
{% extends 'layouts/box.html' %} {% block content %} {% if onboarding %} {% extends 'layouts/box.html' %} {% block content %}
<h1 class="mb-4">Complete your Profile</h1>
{% else %}
<h1 class="mb-4">Edit your Profile</h1>
{% endif %}
<div class="text-center d-flex flex-column align-items-center"> <div class="container">
<img <div class="row justify-content-center">
id="avatar" <div class="col-md-8">
class="w-36 h-36 rounded-circle object-fit-cover my-4" <div class="card" style="border-width: 0px">
src="{{ user.profile.avatar }} " <div class="card-body">
height="144" {% if onboarding %}
/> <h1 class="card-title mb-4">Complete your Profile</h1>
<div class="text-center" style="max-width: 24rem"> {% else %}
<h1 id="displayname">{{ user.profile.displayname|default:"" }}</h1> <h1 class="card-title mb-4">Edit your Profile</h1>
<div class="text-muted mb-2 mt-n3">@{{ user.username }}</div> {% endif %}
<div class="text-center mb-4">
<img
id="avatar"
class="rounded-circle object-cover border"
src="{{ user.profile.avatar }}"
alt="Avatar"
style="width: 150px; height: 150px"
/>
<div class="mt-2">
<h2 id="displayname" class="fw-bold">
{{ user.profile.displayname|default:"" }}
</h2>
<div class="text-muted">@{{ user.username }}</div>
</div>
</div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %} {% for field in form %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label"
>{{ field.label_tag }}</label
>
{{ field }} {% if field.errors %}
<div class="invalid-feedback">
{% for error in field.errors %} {{ error }} {% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Submit</button>
{% if onboarding %}
<a class="btn btn-secondary" href="{% url 'home' %}">Skip</a>
{% else %}
<a
class="btn btn-secondary"
href="{{ request.META.HTTP_REFERER }}"
>Cancel</a
>
{% endif %}
</div>
</form>
</div>
</div>
</div>
</div> </div>
</div> </div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %} {{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
{% if onboarding %}
<a class="btn btn-secondary ms-2" href="{% url 'home' %}">Skip</a>
{% else %}
<a class="btn btn-secondary ms-2" href="{{ request.META.HTTP_REFERER }}"
>Cancel</a
>
{% endif %}
</form>
<script> <script>
// This updates the avatar // Update avatar preview
const fileInput = document.querySelector('input[type="file"]'); const fileInput = document.querySelector('input[type="file"]');
const avatarImage = document.getElementById("avatar");
fileInput.addEventListener("change", (event) => { fileInput.addEventListener("change", (event) => {
const file = event.target.files[0]; const file = event.target.files[0];
const image = document.querySelector("#avatar"); if (file && file.type.startsWith("image/")) {
const reader = new FileReader();
if (file && file.type.includes("image")) { reader.onload = (e) => {
const url = URL.createObjectURL(file); avatarImage.src = e.target.result;
image.src = url; };
reader.readAsDataURL(file);
} }
}); });
// This updates the name // Update display name preview
const display_nameInput = document.getElementById("id_displayname"); const displayNameInput = document.getElementById("id_displayname");
const display_nameOutput = document.getElementById("displayname"); const displayNameOutput = document.getElementById("displayname");
display_nameInput.addEventListener("input", (event) => { displayNameInput.addEventListener("input", (event) => {
display_nameOutput.innerText = event.target.value; displayNameOutput.textContent = event.target.value || "{{ user.username }}"; // Fallback to username if empty
}); });
</script> </script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
static/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -0,0 +1,19 @@
{
"name": "MQTT Ase User Management",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@@ -1,17 +1,45 @@
{% load static %} {% load static %} {% load django_htmx %}
{% load django_htmx %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="it"> <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" />
<meta name="description" content="Django Template"> <meta name="description" content="Django Template" />
<title>Project Title</title> <title>MQTT Ase User Management</title>
<link rel="icon" type="image/x-icon" href="{% static 'favicon.ico' %}"> <link
rel="icon"
type="image/x-icon"
href="{% static 'images/favicon.ico' %}"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="{% static 'images/apple-touch-icon.png' %}"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="{% static 'images/favicon-32x32.png' %}"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="{% static 'images/favicon-16x16.png' %}"
/>
<link rel="icon" href="{% static 'images/favicon.ico' %}" />
<link rel="manifest" href="{% static 'images/site.webmanifest' %}" />
<!-- Bootstrap CSS --> <!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" defer></script> href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
defer
></script>
<!-- HTMX --> <!-- HTMX -->
<script src="https://unpkg.com/htmx.org@2.0.4" defer></script> <script src="https://unpkg.com/htmx.org@2.0.4" defer></script>
@@ -19,18 +47,13 @@
<!-- Custom Styles --> <!-- Custom Styles -->
<style> <style>
.dropdown-menu { min-width: 200px; } .dropdown-menu {
min-width: 200px;
}
</style> </style>
</head> </head>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'> <body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
{% include 'includes/header.html' %} {% include 'includes/messages.html' %}
{% include 'includes/header.html' %} {% block layout %} {% endblock %} {% block javascript %}{% endblock %}
</body>
{% include 'includes/messages.html' %} </html>
{% block layout %}
{% endblock %}
{% block javascript %}{% endblock %}
</body>
</html>

View File

@@ -1,9 +1,9 @@
{% extends 'base.html' %} {% block layout %} {% extends 'base.html' %} {% block layout %}
<div class="container" style="margin-top: 20px;"> <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-md-10 col-lg-8">
<div class="bg-white rounded-3 p-4 p-md-5 shadow-lg"> <div class="card rounded-2xl shadow">
{% block content %} {% endblock %} <div class="card-body p-4">{% block content %} {% endblock %}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,49 +1,49 @@
{% extends 'layouts/blank.html' %} {% block content %} {% extends 'layouts/blank.html' %} {% block content %}
<h1>Add User</h1> <h1>Add User</h1>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="mb-3"> <div class="mb-3">
<label for="site" class="form-label">Site</label> <label for="site" class="form-label">Site</label>
<input type="text" class="form-control" id="site" name="site" required /> <input type="text" class="form-control" id="site" name="site" required />
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="username" class="form-label">Username</label> <label for="username" class="form-label">Username</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
id="username" id="username"
name="username" name="username"
required required
/> />
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="clientId" class="form-label">Client ID</label> <label for="clientId" class="form-label">Client ID</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
id="clientId" id="clientId"
name="clientId" name="clientId"
required required
/> />
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="topic" class="form-label">Topic</label> <label for="topic" class="form-label">Topic</label>
<input type="text" class="form-control" id="topic" name="topic" required /> <input type="text" class="form-control" id="topic" name="topic" required />
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="password" class="form-label">Password</label> <label for="password" class="form-label">Password</label>
<input <input
type="password" type="password"
class="form-control" class="form-control"
id="password" id="password"
name="password" name="password"
required required
/> />
</div> </div>
<button type="submit" class="btn btn-primary">Create</button> <button type="submit" class="btn btn-primary">Create</button>
</form> </form>
<!-- Risultato della creazione --> <!-- Risultato della creazione -->
<div id="result"></div> <div id="result"></div>
{% endblock content%} {% endblock content%}

View File

@@ -48,5 +48,7 @@
/> />
</div> </div>
<button type="submit" class="btn btn-primary">Update</button> <button type="submit" class="btn btn-primary">Update</button>
<a href="{% url 'list_users' %}">
<button type="button" class="btn btn-secondary">Cancel</button>
</form> </form>
{% endblock content %} {% endblock content %}

View File

@@ -14,7 +14,10 @@
<th>Username</th> <th>Username</th>
<th>Client ID</th> <th>Client ID</th>
<th>Topic</th> <th>Topic</th>
<th>Action</th> {% if ase_adm_group %}
<th>Actions</th>
<th> </th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -28,31 +31,22 @@
</td> </td>
<td>{{user.client_id}}</td> <td>{{user.client_id}}</td>
<td>{{user.topic}}</td> <td>{{user.topic}}</td>
<td id="user-status"> {% if ase_adm_group %}
<td>
{% if user.status == "enabled" %} {% if user.status == "enabled" %}
<a <a href="{% url 'disable_user' user.slug %}"
{% if ase_adm_group %} <button class="btn btn-warning btn-sm" >Disable</button>
href="{% url 'disable_user' user.slug %}" </a>
{% endif %}>
<button class="btn btn-danger btn-sm"
{% if not ase_adm_group %}
disabled
{% endif %}
>Disable</button>
</a>
{% else %} {% else %}
<a <a href="{% url 'enable_user' user.slug %}"
{% if ase_adm_group %} <button class="btn btn-info btn-sm" >Enable</button>
href="{% url 'enable_user' user.slug %}" </a>
{% endif %}>
<button class="btn btn-warning btn-sm"
{% if not ase_adm_group %}
disabled
{% endif %}
>Enable</button>
</a>
{% endif %} {% endif %}
<td><a href="{% url 'delete_user' user.slug %}">
<button class="btn btn-danger btn-sm">Delete</button>
</a></td>
</td> </td>
{% endif %}
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>

View File

@@ -26,9 +26,9 @@ SECRET_KEY = 'django-insecure-=-1c582(r8ly33q+-ljg!xb2)#$+a0ykedc4zcj-dqh&i$4$l3
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '*'] ALLOWED_HOSTS = [ 'localhost', '127.0.0.1']
CSRF_TRUSTED_ORIGINS = [ 'https://*' ] CSRF_TRUSTED_ORIGINS = [ 'https://*', 'http://*' ]
# Application definition # Application definition
@@ -44,6 +44,7 @@ INSTALLED_APPS = [
# 'django.contrib.sites', # 'django.contrib.sites',
'allauth', 'allauth',
'allauth.account', 'allauth.account',
'allauth.socialaccount',
'django_htmx', 'django_htmx',
# My apps # My apps

View File

@@ -9,6 +9,7 @@ urlpatterns = [
path('user/list/', views.list_users, name='list_users'), path('user/list/', views.list_users, name='list_users'),
path('user/create_user/', views.create_user, name='create_user'), path('user/create_user/', views.create_user, name='create_user'),
path('user/edit/<slug:slug>', views.edit_user, name='edit_user'), path('user/edit/<slug:slug>', views.edit_user, name='edit_user'),
path('user/delete/<slug:slug>', views.delete_user, name='delete_user'),
path('user/disable/<slug:slug>', views.disable_user, name='disable_user'), path('user/disable/<slug:slug>', views.disable_user, name='disable_user'),
path('user/enable/<slug:slug>', views.enable_user, name='enable_user'), path('user/enable/<slug:slug>', views.enable_user, name='enable_user'),
path('user/role/<str:role>', views.view_role, name='view_role'), path('user/role/<str:role>', views.view_role, name='view_role'),

View File

@@ -1,76 +0,0 @@
import bcrypt
import base64
import hashlib
import json
from cryptography.fernet import Fernet
from .models import MasterHash
from django.http import JsonResponse
# 1. Salva l'hash della master password e la chiave principale cifrata
def save_master_data(hashed_password, encrypted_data_key):
entry, created = MasterHash.objects.get_or_create(id="1")
entry.hash = hashed_password
entry.encrypted_data_key = encrypted_data_key
entry.save()
# 2. Carica i dati della master password
def load_master_data():
try:
entry = MasterHash.objects.get(id="1")
return entry.hash, entry.encrypted_data_key
except Exception:
return None, None
# 3. Autenticazione della master password
def authenticate(master_password):
stored_hash, encrypted_data_key = load_master_data()
if stored_hash is None:
hashed_password = bcrypt.hashpw(master_password.encode(), bcrypt.gensalt())
key = Fernet.generate_key() # Genera una chiave principale
derived_key = derive_key(master_password) # Deriva la chiave dalla master password
encrypted_data_key = encrypt_password(key.decode(), derived_key) # Cifra la chiave principale
save_master_data(hashed_password, encrypted_data_key)
return True, key
# Controlla se la password inserita è corretta
if bcrypt.checkpw(master_password.encode(), stored_hash.tobytes()):
derived_key = derive_key(master_password)
decrypted_data_key = decrypt_password(encrypted_data_key, derived_key) # Decifra la chiave principale
return True, decrypted_data_key.encode()
return False, None
# 4. Funzione per cambiare la master password
def change_master_password(old_password, new_password):
authenticated, data_key = authenticate(old_password)
if not authenticated:
return False # Fallisce se la vecchia password non è corretta
# Deriviamo la nuova chiave dalla nuova master password
new_derived_key = derive_key(new_password)
# Cifriamo la chiave principale con la nuova chiave derivata
new_encrypted_data_key = encrypt_password(data_key.decode(), new_derived_key)
# Creiamo il nuovo hash della nuova master password
new_hashed_password = bcrypt.hashpw(new_password.encode(), bcrypt.gensalt())
# Aggiorniamo il database con i nuovi dati
save_master_data(new_hashed_password, new_encrypted_data_key)
return True # Cambio password riuscito
# 5. Deriva una chiave da una password
def derive_key(master_password):
hash = hashlib.sha256(master_password.encode()).digest()
return base64.urlsafe_b64encode(hash)
# 6. Cifra una password con una chiave
def encrypt_password(password, key):
cipher = Fernet(key)
return cipher.encrypt(password.encode()).decode()
# 7. Decifra una password con una chiave
def decrypt_password(encrypted_password, key):
cipher = Fernet(key)
return cipher.decrypt(encrypted_password.encode()).decode()

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, csrf_protect
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required
import json import json
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
@@ -16,13 +16,51 @@ from .mqtt_service import MosquittoDynamicSecurity
config = dotenv_values(".env") config = dotenv_values(".env")
# authenticate(config['MASTER_PASSWORD'])
@csrf_exempt
def list_sites_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
sites = PasswordEntry.objects.values_list('site', flat=True)
return JsonResponse({"sites": list(sites)})
return JsonResponse({"error": "Richiesta non valida"}, status=400)
@csrf_exempt
def get_password_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
site = data.get('site')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
key = derive_key(master_password)
try:
entry = PasswordEntry.objects.get(site=site)
decrypted_password = decrypt_password(entry.password, key)
return JsonResponse({
"site": entry.site,
"username": entry.username,
"password": decrypted_password,
"client_id": entry.client_id,
"topic": entry.topic
})
except PasswordEntry.DoesNotExist:
return JsonResponse({"error": "Sito non trovato"}, status=404)
return JsonResponse({"error": "Richiesta non valida"}, status=400)
@login_required
def home_view(request): def home_view(request):
return redirect('list_users') # Reindirizza alla lista degli utenti return redirect('list_users') # Reindirizza alla lista degli utenti
#return render(request, 'home.html') #return render(request, 'home.html')
@csrf_exempt @login_required
def publish_message(request): def publish_message(request):
request_data = json.loads(request.body) request_data = json.loads(request.body)
publish.single(topic=request_data['topic'], publish.single(topic=request_data['topic'],
@@ -58,6 +96,57 @@ def create_user(request):
"username": username, "username": username,
"password": password "password": password
} }
],
"commands": [
{
"command": "createRole",
"rolename": f'{username}_role',
"textname": "",
"textdescription": "",
"acls": [
{ "acltype": "publishClientSend", "topic": topic, "priority": 0, "allow": True }
]
},
{
"command": "createRole",
"rolename": f'{username}_ase_role',
"textname": "",
"textdescription": "",
"acls": [
{ "acltype": "publishClientSend", "topic": topic, "priority": 0, "allow": True },
{ "acltype": "publishClientReceive", "topic": topic, "priority": 0, "allow": True },
{ "acltype": "subscribeLiteral", "topic": topic, "priority": 0, "allow": True },
{ "acltype": "subscribePattern", "topic": topic, "priority": 0, "allow": True },
{ "acltype": "unsubscribeLiteral", "topic": topic, "priority": 0, "allow": True },
{ "acltype": "unsubscribePattern", "topic": topic, "priority": 0, "allow": True }
]
},
{
"command": "createClient",
"username": username,
"password": password,
"clientid": client_id,
"textname": f'{username} subscriber',
"textdescription": f'{username} subscriber',
"groups": [
],
"roles": [
{ "rolename": f'{username}_role', "priority": 0 }
]
},
{
"command": "createClient",
"username": f'{username}_ase',
"password": f'{password}{config["MQTT_PWDX"]}',
"clientid": f'{client_id}_ase',
"textname": f'{username} ASE subscriber',
"textdescription": f'{username} ASE subscriber',
"groups": [
],
"roles": [
{ "rolename": f'{username}_ase_role', "priority": 0 }
]
}
] ]
} }
@@ -68,6 +157,7 @@ def create_user(request):
key = derive_key(config['MASTER_PASSWORD']) key = derive_key(config['MASTER_PASSWORD'])
encrypted_password = encrypt_password(password, key) encrypted_password = encrypt_password(password, key)
encrypted_password_ase = encrypt_password(f'{password}_ase', key)
PasswordEntry.objects.create( PasswordEntry.objects.create(
site=site, site=site,
@@ -78,6 +168,15 @@ def create_user(request):
status='enabled' status='enabled'
) )
PasswordEntry.objects.create(
site=f'{site}_ase',
username=f'{username}_ase',
password=encrypted_password_ase,
client_id=f'{client_id}_ase',
topic=topic,
status='enabled'
)
messages.success(request, 'Utente creato con successo!') # Messaggio di successo messages.success(request, 'Utente creato con successo!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti return redirect('list_users') # Reindirizza alla lista degli utenti
else: else:
@@ -96,9 +195,39 @@ def edit_user(request, slug):
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: else:
messages.success(request, 'Non hai i permessi per creare utenti MQTT!') # Messaggio di successo messages.success(request, 'Non hai i permessi per cancellare utenti MQTT!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti return redirect('list_users') # Reindirizza alla lista degli utenti
@login_required
def delete_user(request, slug):
if request.user.groups.filter(name='ase_admin').exists():
user = PasswordEntry.objects.filter(slug=slug).first()
command = {
"commands":
[
{
"command": "deleteClient",
"username": user.username
},
{
"command": "deleteRole",
"rolename": f'{user.username}_role'
}
]
}
# Invia il comando a Mosquitto
mqtt_service = MosquittoDynamicSecurity()
response = mqtt_service.send_command(command)
if "error" not in response["responses"][0]:
result = PasswordEntry.objects.filter(id=user.id).delete()
print(result)
messages.success(request, f'Utente {user.username} eliminato!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
else:
messages.success(request, 'Non hai i permessi per cancellare utenti MQTT!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti
@login_required @login_required
def disable_user(request, slug): def disable_user(request, slug):
if request.user.groups.filter(name='ase_admin').exists(): if request.user.groups.filter(name='ase_admin').exists():
@@ -164,43 +293,9 @@ def enable_user(request, slug):
messages.success(request, 'Non hai i permessi per disabilitare utenti MQTT!') # Messaggio di successo messages.success(request, 'Non hai i permessi per disabilitare utenti MQTT!') # Messaggio di successo
return redirect('list_users') # Reindirizza alla lista degli utenti return redirect('list_users') # Reindirizza alla lista degli utenti
@login_required
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 })
@csrf_exempt
def get_password_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
site = data.get('site')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
key = derive_key(master_password)
try:
entry = PasswordEntry.objects.get(site=site)
decrypted_password = decrypt_password(entry.password, key)
return JsonResponse({
"site": entry.site,
"username": entry.username,
"password": decrypted_password,
"client_id": entry.client_id,
"topic": entry.topic
})
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):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
sites = PasswordEntry.objects.values_list('site', flat=True)
return JsonResponse({"sites": list(sites)})
return JsonResponse({"error": "Richiesta non valida"}, status=400)