command check - fix subscriber
This commit is contained in:
7
.env
7
.env
@@ -3,3 +3,10 @@ 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
|
||||||
|
MQTT_HOST=10.211.114.214
|
||||||
|
MQTT_PORT=1883
|
||||||
|
MQTT_KEEPALIVE=60
|
||||||
|
MQTT_USER=alex
|
||||||
|
MQTT_PASSWORD=batt1l0
|
||||||
|
MQTT_DS_TOPIC="$CONTROL/dynamic-security/v1"
|
||||||
|
MQTT_DS_RESP_TOPIC="$CONTROL/dynamic-security/v1/response"
|
||||||
|
|||||||
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -11,7 +11,8 @@
|
|||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"args": [
|
"args": [
|
||||||
"runserver"
|
"runserver",
|
||||||
|
"--noreload",
|
||||||
],
|
],
|
||||||
"django": true,
|
"django": true,
|
||||||
"autoStartBrowser": false,
|
"autoStartBrowser": false,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ dependencies = [
|
|||||||
"cryptography>=44.0.0",
|
"cryptography>=44.0.0",
|
||||||
"django>=5.1.4",
|
"django>=5.1.4",
|
||||||
"django-htmx>=1.21.0",
|
"django-htmx>=1.21.0",
|
||||||
|
"gunicorn>=23.0.0",
|
||||||
"paho-mqtt>=2.1.0",
|
"paho-mqtt>=2.1.0",
|
||||||
"psycopg2-binary>=2.9.10",
|
"psycopg2-binary>=2.9.10",
|
||||||
"python-dotenv>=1.0.1",
|
"python-dotenv>=1.0.1",
|
||||||
|
|||||||
23
uv.lock
generated
23
uv.lock
generated
@@ -140,6 +140,7 @@ dependencies = [
|
|||||||
{ name = "cryptography" },
|
{ name = "cryptography" },
|
||||||
{ name = "django" },
|
{ name = "django" },
|
||||||
{ name = "django-htmx" },
|
{ name = "django-htmx" },
|
||||||
|
{ name = "gunicorn" },
|
||||||
{ name = "paho-mqtt" },
|
{ name = "paho-mqtt" },
|
||||||
{ name = "psycopg2-binary" },
|
{ name = "psycopg2-binary" },
|
||||||
{ name = "python-dotenv" },
|
{ name = "python-dotenv" },
|
||||||
@@ -151,11 +152,33 @@ requires-dist = [
|
|||||||
{ name = "cryptography", specifier = ">=44.0.0" },
|
{ name = "cryptography", specifier = ">=44.0.0" },
|
||||||
{ name = "django", specifier = ">=5.1.4" },
|
{ name = "django", specifier = ">=5.1.4" },
|
||||||
{ name = "django-htmx", specifier = ">=1.21.0" },
|
{ name = "django-htmx", specifier = ">=1.21.0" },
|
||||||
|
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||||
{ name = "paho-mqtt", specifier = ">=2.1.0" },
|
{ name = "paho-mqtt", specifier = ">=2.1.0" },
|
||||||
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
|
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
|
||||||
{ name = "python-dotenv", specifier = ">=1.0.1" },
|
{ name = "python-dotenv", specifier = ">=1.0.1" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gunicorn"
|
||||||
|
version = "23.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "packaging" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "24.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paho-mqtt"
|
name = "paho-mqtt"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
from queue import Queue
|
||||||
|
from dotenv import dotenv_values
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
print(f"__init__.py eseguito in PID {os.getpid()}")
|
||||||
|
|
||||||
|
config = dotenv_values(".env")
|
||||||
|
|
||||||
|
listClients_queue = Queue()
|
||||||
|
listRoles_queue = Queue()
|
||||||
|
getClient_queue = Queue()
|
||||||
|
changeClient_queue = Queue()
|
||||||
|
|
||||||
|
command_queue_map = {
|
||||||
|
"listClients": listClients_queue,
|
||||||
|
"listRoles": listRoles_queue,
|
||||||
|
"getClient": getClient_queue,
|
||||||
|
"createClient": changeClient_queue,
|
||||||
|
"deleteClient": changeClient_queue,
|
||||||
|
"enableClient": changeClient_queue,
|
||||||
|
"disableClient": changeClient_queue
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_message(client, userdata, msg):
|
||||||
|
msg_json = json.loads(msg.payload.decode("utf-8"))
|
||||||
|
command = msg_json['responses'][0]['command']
|
||||||
|
print(f"Received message: {msg.payload.decode("utf-8")} - Command: {command}")
|
||||||
|
|
||||||
|
if command in command_queue_map:
|
||||||
|
command_queue_map[command].put(msg.payload.decode("utf-8"))
|
||||||
|
|
||||||
|
def on_connect(client, userdata, flags, rc, properties):
|
||||||
|
if rc == 0:
|
||||||
|
print('Connected successfully. Properties:', properties)
|
||||||
|
client.subscribe(config['MQTT_DS_RESP_TOPIC'])
|
||||||
|
else:
|
||||||
|
print('Bad connection. Code:', rc)
|
||||||
|
|
||||||
|
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, protocol=mqtt.MQTTv5)
|
||||||
|
client.on_connect = on_connect
|
||||||
|
client.on_message = on_message
|
||||||
|
client.username_pw_set(config['MQTT_USER'], config['MQTT_PASSWORD'])
|
||||||
|
client.connect(config['MQTT_HOST'], int(config['MQTT_PORT']), int(config['MQTT_KEEPALIVE']))
|
||||||
|
client.loop_start()
|
||||||
@@ -2,4 +2,7 @@ from django.contrib import admin
|
|||||||
from .models import PasswordEntry
|
from .models import PasswordEntry
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
admin.site.register(PasswordEntry)
|
class PasswordEntryAdmin(admin.ModelAdmin):
|
||||||
|
prepopulated_fields = {"slug": ("site", "username")}
|
||||||
|
|
||||||
|
admin.site.register(PasswordEntry, PasswordEntryAdmin)
|
||||||
|
|||||||
0
wallet_api/management/__init__.py
Normal file
0
wallet_api/management/__init__.py
Normal file
0
wallet_api/management/commands/__init__.py
Normal file
0
wallet_api/management/commands/__init__.py
Normal file
0
wallet_api/management/commands/_private.py
Normal file
0
wallet_api/management/commands/_private.py
Normal file
54
wallet_api/management/commands/check_data_integrity.py
Normal file
54
wallet_api/management/commands/check_data_integrity.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from wallet_api.models import PasswordEntry
|
||||||
|
import json
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Check data alignament across DB and Dynamic Security mosquitto file"
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
"dynsecjson",
|
||||||
|
nargs="+",
|
||||||
|
type=str,
|
||||||
|
help="Mosquitto dynamic security json file",
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
for dynsec in options["dynsecjson"]:
|
||||||
|
try:
|
||||||
|
with open(dynsec, "r") as dynsecconfig:
|
||||||
|
config = json.load(dynsecconfig)
|
||||||
|
for client in config['clients']:
|
||||||
|
if client['username'] == 'admin':
|
||||||
|
continue
|
||||||
|
user_name = client['username']
|
||||||
|
try:
|
||||||
|
password_entry = PasswordEntry.objects.get(username=user_name)
|
||||||
|
if password_entry:
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'username {user_name} exists in the database.'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.WARNING(f'Username {user_name} does not exist in the database.'))
|
||||||
|
except PasswordEntry.DoesNotExist:
|
||||||
|
self.stdout.write(self.style.WARNING(f'Username {user_name} does not exist in the database.'))
|
||||||
|
|
||||||
|
for role in config['roles']:
|
||||||
|
if role['rolename'] == 'admin':
|
||||||
|
continue
|
||||||
|
role_name = role['rolename']
|
||||||
|
try:
|
||||||
|
password_entry = PasswordEntry.objects.get(role=role_name)
|
||||||
|
if password_entry:
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'Role {role_name} exists in the database.'))
|
||||||
|
acls_db = password_entry.acls
|
||||||
|
acls_json = role.get('acls', [])
|
||||||
|
for acltype_json in acls_json:
|
||||||
|
if acltype_json['acltype'] not in [d['acltype'] for d in acls_db]:
|
||||||
|
self.stdout.write(self.style.WARNING(f'ACL type {acltype_json["acltype"]} for role {role_name} does not exist in the database.'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.WARNING(f'Role {role_name} does not exist in the database.'))
|
||||||
|
except PasswordEntry.DoesNotExist:
|
||||||
|
self.stdout.write(self.style.WARNING(f'Role {role_name} does not exist in the database.'))
|
||||||
|
except Exception as e:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Check data alignament across DB and Dynamic Security mosquitto file {dynsec} - {e}'))
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.1.4 on 2025-01-06 10:24
|
# Generated by Django 5.1.4 on 2025-01-15 18:24
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
@@ -28,6 +28,10 @@ class Migration(migrations.Migration):
|
|||||||
('password', models.TextField()),
|
('password', models.TextField()),
|
||||||
('client_id', models.CharField(max_length=255)),
|
('client_id', models.CharField(max_length=255)),
|
||||||
('topic', models.CharField(max_length=255)),
|
('topic', models.CharField(max_length=255)),
|
||||||
|
('role', models.CharField(max_length=255)),
|
||||||
|
('acls', models.JSONField()),
|
||||||
|
('status', models.CharField(max_length=255)),
|
||||||
|
('slug', models.SlugField(default='', null=True)),
|
||||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ class PasswordEntry(models.Model):
|
|||||||
password = models.TextField()
|
password = models.TextField()
|
||||||
client_id = models.CharField(max_length=255)
|
client_id = models.CharField(max_length=255)
|
||||||
topic = models.CharField(max_length=255)
|
topic = models.CharField(max_length=255)
|
||||||
|
role = models.CharField(max_length=255)
|
||||||
|
acls = models.JSONField()
|
||||||
|
status = models.CharField(max_length=255)
|
||||||
|
slug = models.SlugField(default="", null=True)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
32
wallet_api/mqtt.py
Normal file
32
wallet_api/mqtt.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
|
MQTT_SERVER="10.211.114.214"
|
||||||
|
MQTT_PORT=1883
|
||||||
|
MQTT_KEEPALIVE=60
|
||||||
|
MQTT_USER="pippo"
|
||||||
|
MQTT_PASSWORD="batt1l0"
|
||||||
|
MQTT_DS_TOPIC="$CONTROL/dynamic-security/v1"
|
||||||
|
|
||||||
|
def on_connect(mqtt_client, userdata, flags, rc, properties):
|
||||||
|
if rc == 0:
|
||||||
|
print('Connected successfully. Properties:', properties)
|
||||||
|
mqtt_client.subscribe(MQTT_TOPIC)
|
||||||
|
else:
|
||||||
|
print('Bad connection. Code:', rc)
|
||||||
|
|
||||||
|
def on_disconnect(mqtt_client, obj, flags, rc, properties):
|
||||||
|
print('disconnected. Code:', rc)
|
||||||
|
|
||||||
|
def on_message(mqtt_client, userdata, msg):
|
||||||
|
print(f'Received message on topic: {msg.topic} with payload: {msg.payload}')
|
||||||
|
|
||||||
|
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, protocol=mqtt.MQTTv5)
|
||||||
|
client.on_connect = on_connect
|
||||||
|
client.on_disconnect = on_disconnect
|
||||||
|
client.on_message = on_message
|
||||||
|
client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
|
||||||
|
client.connect(
|
||||||
|
host=MQTT_SERVER,
|
||||||
|
port=MQTT_PORT,
|
||||||
|
keepalive=MQTT_KEEPALIVE
|
||||||
|
)
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<link
|
<link
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
|
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -22,15 +22,15 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block container %}
|
{% block container %}
|
||||||
<main role="main" class="container">
|
<main role="main" class="container mt-4">
|
||||||
{% block content %} {% endblock content %}
|
{% block content %} {% endblock content %}
|
||||||
</main>
|
</main>
|
||||||
{% endblock container %} {% block footer %} {% endblock footer %}
|
{% endblock container %} {% block footer %} {% endblock footer %}
|
||||||
|
|
||||||
<!-- Bootstrap Javascript -->
|
<!-- Bootstrap Javascript -->
|
||||||
<script
|
<script
|
||||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
|
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
|
||||||
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
|
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
></script>
|
></script>
|
||||||
|
|
||||||
|
|||||||
48
wallet_api/templates/wallet_api/add_user.html
Normal file
48
wallet_api/templates/wallet_api/add_user.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{% extends "base.html" %} {% load static %} {% block content %}
|
||||||
|
<h1>Add User</h1>
|
||||||
|
<form hx-post="/create_user" hx-target="#content">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="site" class="form-label">Site</label>
|
||||||
|
<input type="text" class="form-control" id="site" name="site" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="clientId" class="form-label">Client ID</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="clientId"
|
||||||
|
name="clientId"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="topic" class="form-label">Topic</label>
|
||||||
|
<input type="text" class="form-control" id="topic" name="topic" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="role" class="form-label">Role</label>
|
||||||
|
<input type="text" class="form-control" id="role" name="role" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Create</button>
|
||||||
|
</form>
|
||||||
|
{% endblock content %}
|
||||||
62
wallet_api/templates/wallet_api/edit_user.html
Normal file
62
wallet_api/templates/wallet_api/edit_user.html
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{% extends "base.html" %} {% load static %} {% block content %}
|
||||||
|
<h1>Edit User</h1>
|
||||||
|
<form hx-post="/update_user" hx-target="#content">
|
||||||
|
<input type="hidden" name="id" value="{{ user.id }}" />
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="site" class="form-label">Site</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="site"
|
||||||
|
name="site"
|
||||||
|
value="{{ user.site }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
value="{{ user.username }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="clientId" class="form-label">Client ID</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="clientId"
|
||||||
|
name="clientId"
|
||||||
|
value="{{ user.client_id }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="topic" class="form-label">Topic</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="topic"
|
||||||
|
name="topic"
|
||||||
|
value="{{ user.topic }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="role" class="form-label">Role</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="role"
|
||||||
|
name="role"
|
||||||
|
value="{{ user.role }}"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
|
</form>
|
||||||
|
{% endblock content %}
|
||||||
@@ -1,23 +1,60 @@
|
|||||||
{% extends "base.html" %} {% load static %} {% block content %}
|
{% extends "base.html" %} {% load static %} {% block content %}
|
||||||
<h1>List Mosquitto Users</h1>
|
<h1>List Mosquitto Users</h1>
|
||||||
<table class="table">
|
<button class="btn btn-primary mb-3" hx-get="/add_user" hx-target="#content">
|
||||||
|
Add User
|
||||||
|
</button>
|
||||||
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">#</th>
|
<th>Site</th>
|
||||||
<th scope="col">Site</th>
|
<th>Username</th>
|
||||||
<th scope="col">Username</th>
|
<th>Client ID</th>
|
||||||
<th scope="col">Client ID</th>
|
<th>Topic</th>
|
||||||
<th scope="col">Topic</th>
|
<th>Role</th>
|
||||||
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{{ user.id }}</th>
|
<td>{{user.site}}</td>
|
||||||
<td>{{ user.site }}</td>
|
<td>
|
||||||
<td>{{ user.username }}</td>
|
<a
|
||||||
<td>{{ user.client_id }}</td>
|
href="/wallet/user/edit/{{user.id}}"
|
||||||
<td>{{ user.topic }}</td>
|
hx-get="/wallet/user/edit/{{user.id}}"
|
||||||
|
hx-target="#content"
|
||||||
|
>{{user.username}}</a
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
<td>{{user.client_id}}</td>
|
||||||
|
<td>{{user.topic}}</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href="/wallet/user/role/{{user.role}}"
|
||||||
|
hx-get="/wallet/user/role/{{user.role}}"
|
||||||
|
hx-target="#content"
|
||||||
|
>{{user.role}}</a
|
||||||
|
>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if user.status == "enabled" %}
|
||||||
|
<button
|
||||||
|
class="btn btn-danger btn-sm"
|
||||||
|
hx-post="/wallet/user/disable/{{user.id}}"
|
||||||
|
hx-target="#user-list"
|
||||||
|
>
|
||||||
|
Disable
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<button
|
||||||
|
class="btn btn-warning btn-sm"
|
||||||
|
hx-post="/wallet/user/enable/{{user.id}}"
|
||||||
|
hx-target="#user-list"
|
||||||
|
>
|
||||||
|
Enable
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<i> No users </i>
|
<i> No users </i>
|
||||||
|
|||||||
20
wallet_api/templates/wallet_api/role_info.html
Normal file
20
wallet_api/templates/wallet_api/role_info.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{% extends "base.html" %} {% load static %} {% block content %}
|
||||||
|
<h1>Role Permissions</h1>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Permission</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- Role permissions will be dynamically loaded here -->
|
||||||
|
{{#each permissions}}
|
||||||
|
<tr>
|
||||||
|
<td>{{ this.permission }}</td>
|
||||||
|
<td>{{ this.description }}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock content %}
|
||||||
@@ -2,9 +2,13 @@ from django.urls import path
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('add/', views.add_password_api, name='add_password'),
|
path('add/', views.add_password_api, name='add_user'),
|
||||||
|
path('disable/', views.disable_password_api, name='disable_user'),
|
||||||
path('get/', views.get_password_api, name='get_password'),
|
path('get/', views.get_password_api, name='get_password'),
|
||||||
path('delete/', views.delete_password_api, name='delete_password'),
|
|
||||||
path('list/', views.list_sites_api, name='list_sites'),
|
path('list/', views.list_sites_api, name='list_sites'),
|
||||||
path('user/list/', views.list_users, name='list_users'),
|
path('user/list/', views.list_users, name='list_users'),
|
||||||
|
path('user/add/', views.add_user, name='add_user'),
|
||||||
|
path('user/edit/<int:user_id>', views.edit_user, name='edit_user'),
|
||||||
|
path('user/role/<str:role>', views.view_role, name='view_role'),
|
||||||
|
path('publish', views.publish_message, name='publish'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,14 +3,43 @@ from django.shortcuts import render
|
|||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from .models import PasswordEntry, MasterHash
|
from .models import PasswordEntry, MasterHash
|
||||||
from .utils import authenticate, derive_key, encrypt_password, decrypt_password
|
from .utils import authenticate, derive_key, encrypt_password, decrypt_password
|
||||||
|
from dotenv import dotenv_values
|
||||||
import json
|
import json
|
||||||
|
import threading
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import paho.mqtt.publish as publish
|
||||||
|
|
||||||
|
config = dotenv_values(".env")
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def publish_message(request):
|
||||||
|
request_data = json.loads(request.body)
|
||||||
|
publish.single(topic=request_data['topic'],
|
||||||
|
payload=json.dumps(request_data['msg']),
|
||||||
|
hostname=config['MQTT_HOST'],
|
||||||
|
port=int(config['MQTT_PORT']),
|
||||||
|
keepalive=int(config['MQTT_KEEPALIVE']),
|
||||||
|
auth={'username': config['MQTT_USER'], 'password': config['MQTT_PASSWORD']},
|
||||||
|
protocol=mqtt.MQTTv5)
|
||||||
|
return JsonResponse({'request_data': request_data})
|
||||||
|
|
||||||
def list_users(request):
|
def list_users(request):
|
||||||
users = PasswordEntry.objects.all()
|
users = PasswordEntry.objects.all()
|
||||||
|
lock_users = threading.Lock()
|
||||||
return render(request, 'wallet_api/list_users.html', {'users': users})
|
return render(request, 'wallet_api/list_users.html', {'users': users})
|
||||||
|
|
||||||
|
def edit_user(request, user_id):
|
||||||
|
user = PasswordEntry.objects.filter(id=user_id).first()
|
||||||
|
return render(request, 'wallet_api/edit_user.html', {'user': user})
|
||||||
|
|
||||||
|
def add_user(request):
|
||||||
|
return render(request, 'wallet_api/add_user.html')
|
||||||
|
|
||||||
|
def view_role(request, role):
|
||||||
|
return render(request, 'wallet_api/role_info.html', {'role': role })
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def add_password_api(request):
|
def disable_password_api(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
data = json.loads(request.body)
|
data = json.loads(request.body)
|
||||||
master_password = data.get('master_password')
|
master_password = data.get('master_password')
|
||||||
@@ -59,23 +88,6 @@ 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)
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
def delete_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)
|
|
||||||
|
|
||||||
try:
|
|
||||||
entry = PasswordEntry.objects.get(site=site)
|
|
||||||
entry.delete()
|
|
||||||
return JsonResponse({"message": "Password cancellata con successo"})
|
|
||||||
except PasswordEntry.DoesNotExist:
|
|
||||||
return JsonResponse({"error": "Sito non trovato"}, status=404)
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def list_sites_api(request):
|
def list_sites_api(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -87,3 +99,31 @@ 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)})
|
||||||
|
|
||||||
|
@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,
|
||||||
|
role='',
|
||||||
|
acls='{}'
|
||||||
|
)
|
||||||
|
return JsonResponse({"message": "Password aggiunta con successo"})
|
||||||
|
|||||||
Reference in New Issue
Block a user