mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
The Logs page now records every change an admin makes to a compute's libvirt objects, with the compute's name in the host column: - networks: add, start, stop, delete, autostart, DHCP range, fixed addresses, QoS and XML edits; - storage pools: add, start, stop, delete, autostart, from web and API; volumes: add and delete (web and API), clone and ISO upload; - host interfaces: add, start, stop, delete; - secrets: add, set value (never the value), delete; - network filters: add, clone, delete, XML edits, rules and references. A refused or failed change is a failed row with its reason (libvirt text through safe_text). The volume clone and upload jobs write their row when they end; a refused submission is a failed row at once. A volume row names the volume libvirt created (a dir pool adds .qcow2 or .img). Volume create or delete through the API on an inactive pool (409) is a failed row. Network filters: the rows read "Network filter f1: add" instead of "f1 network filter is created", and their host is the compute's name instead of its hostname. A duplicate name or UUID no longer raises a bare exception, and a filter XML that cannot be parsed no longer ends in UnboundLocalError.
57 lines
2.7 KiB
Python
57 lines
2.7 KiB
Python
"""Secret changes write rows on the Logs page; a secret's value never does."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
from computes.models import Compute
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
from vrtManager.util import OperationError
|
|
|
|
from logs.models import Logs
|
|
|
|
UUID = "8f2c0000-0000-4000-8000-000000000009"
|
|
VALUE = "s3cr3t-value"
|
|
|
|
|
|
def rows():
|
|
return list(Logs.objects.order_by("id").values_list("message", "success"))
|
|
|
|
|
|
class SecretRowTests(TestCase):
|
|
def setUp(self):
|
|
self.compute = Compute.objects.create(name="c1", hostname="localhost", login="", password="", type=4)
|
|
self.client.force_login(get_user_model().objects.create_superuser("audit-admin", "a@example.com", "pw"))
|
|
Logs.objects.all().delete() # the login's row
|
|
self.url = reverse("virtsecrets", args=[self.compute.id])
|
|
patcher = patch("virtsecrets.views.wvmSecrets")
|
|
self.conn = patcher.start().return_value
|
|
self.addCleanup(patcher.stop)
|
|
self.conn.get_secrets.return_value = []
|
|
|
|
def test_secret_actions(self):
|
|
self.client.post(self.url, {"create": "", "ephemeral": "no", "private": "yes", "usage_type": "ceph",
|
|
"data": "client.admin"})
|
|
self.client.post(self.url, {"set_value": "", "uuid": UUID, "value": VALUE})
|
|
self.client.post(self.url, {"delete": "", "uuid": UUID})
|
|
self.assertEqual(rows(), [
|
|
("Secret ceph client.admin: add", True),
|
|
(f"Secret {UUID}: set value", True),
|
|
(f"Secret {UUID}: delete", True),
|
|
])
|
|
self.assertEqual(set(Logs.objects.values_list("host", flat=True)), {"c1"})
|
|
|
|
def test_a_secret_value_is_never_logged(self):
|
|
self.conn.set_secret_value.side_effect = ValueError(f"bad value {VALUE}")
|
|
self.client.post(self.url, {"set_value": "", "uuid": UUID, "value": VALUE})
|
|
self.assertEqual(rows(), [(f"Secret {UUID}: set value: ValueError", False)])
|
|
with patch.object(Logs.objects, "create", side_effect=RuntimeError("db down")), \
|
|
self.assertLogs("logs.audit", "WARNING") as logged:
|
|
self.client.post(self.url, {"set_value": "", "uuid": UUID, "value": VALUE})
|
|
self.assertNotIn(VALUE, "\n".join(logged.output))
|
|
self.assertIn(f"Secret {UUID}: set value", "\n".join(logged.output))
|
|
|
|
def test_a_refused_secret_delete(self):
|
|
self.conn.delete_secret.side_effect = OperationError(f"Secret {UUID} is used by VMs db-2")
|
|
self.client.post(self.url, {"delete": "", "uuid": UUID})
|
|
self.assertEqual(rows(), [(f"Secret {UUID}: delete: Secret {UUID} is used by VMs db-2", False)])
|