mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 03:36:48 +00:00
The Logs page now records the panel's own records too: users (add, change with the changed fields, delete, block, unblock, an admin's password set, a user's own password change including the first-login one, own profile edits), groups with their members and permissions, VM owners from the accounts pages and from the VM page, OTP QR code mails, flavors from web and API, app settings and the theme, removed-VM records and SSH keys. No row carries a password or a key. - A user is created in one transaction: when a VM named in NEW_USER_DEFAULT_INSTANCES is missing, no half-made user stays. - The OTP mail checks the email address before it creates a device; without one it writes a failed row and makes no device. - The VM page's owner rows read "VM web-1 owner bob: add" and "...: remove" instead of "Add owner: bob" and "Delete owner: 17"; a second owner refused by the single-owner setting is a failed row. Owner rows keep the VM's host and name, so the VM's log tab shows them. - A setting or theme posted with its current value is no change and writes no row; the old free-text setting rows are gone. Host-side objects (networks, storage, interfaces, secrets, filters) follow in the next layer.
103 lines
4.9 KiB
Python
103 lines
4.9 KiB
Python
"""Changes to a user's own account, VM ownership, OTP mails and SSH keys write rows on the Logs page."""
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
|
|
from accounts.models import UserAttributes
|
|
from logs.models import Logs
|
|
|
|
|
|
def rows():
|
|
return list(Logs.objects.order_by("id").values_list("message", "success"))
|
|
|
|
|
|
class OwnAccountRowTests(TestCase):
|
|
def setUp(self):
|
|
self.bob = get_user_model().objects.create_user("bob", password="Bob-pass-2026")
|
|
self.client.force_login(self.bob)
|
|
Logs.objects.all().delete() # the login's row
|
|
|
|
def test_own_profile_and_password(self):
|
|
self.client.post(reverse("accounts:profile"), {"username": "bob", "email": "bob@example.com"})
|
|
self.client.post(reverse("accounts:change_password"), {
|
|
"old_password": "Bob-pass-2026", "new_password1": "Bob-new-pass-1", "new_password2": "Bob-new-pass-1"})
|
|
self.assertEqual(rows(), [("User bob: change email", True), ("User bob: change password", True)])
|
|
self.assertFalse(Logs.objects.filter(message__contains="pass-").exists())
|
|
|
|
def test_the_first_login_password_change(self):
|
|
self.bob.user_permissions.clear()
|
|
UserAttributes.objects.update_or_create(user=self.bob, defaults={"must_change_password": True})
|
|
self.client.post(reverse("accounts:change_password"), {
|
|
"old_password": "Bob-pass-2026", "new_password1": "Bob-new-pass-1", "new_password2": "Bob-new-pass-1"})
|
|
self.assertEqual(rows(), [("User bob: change password", True)])
|
|
|
|
|
|
SSH_KEY = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6OOdbfv27QVnSC6sKxGaHb6YFc+3gxCkyVR3cTSXE/n5BEGf8aOgBpepULWa1RZfx"
|
|
"YHY14PlKULDygdXSdrrR2kNSwoKz/Oo4d+3EE92L7ocl1+djZbptzgWgtw1OseLwbFik+iKlIdqPsH+IUQvX7yV545ZQtAP8Qj1R+uCqkw=="
|
|
" test@test")
|
|
|
|
|
|
class AdminAccountRowTests(TestCase):
|
|
def setUp(self):
|
|
from computes.models import Compute
|
|
from instances.models import Instance
|
|
|
|
self.admin = get_user_model().objects.create_superuser("audit-admin", "audit@example.com", "pw")
|
|
self.bob = get_user_model().objects.create_user("bob", password="Bob-pass-2026")
|
|
compute = Compute.objects.create(name="c1", hostname="127.0.0.1", login="", password="", type=4)
|
|
self.vm = Instance.objects.create(compute=compute, name="vm", uuid="66666666-6666-6666-6666-666666666666")
|
|
self.client.force_login(self.admin)
|
|
Logs.objects.all().delete() # the login's row
|
|
|
|
def test_ownership_from_the_accounts_pages(self):
|
|
from accounts.models import UserInstance
|
|
|
|
self.client.post(reverse("accounts:user_instance_create", args=[self.bob.pk]),
|
|
{"user": self.bob.pk, "instance": self.vm.pk})
|
|
ui = UserInstance.objects.get(user=self.bob, instance=self.vm)
|
|
url = reverse("accounts:user_instance_update", args=[ui.pk])
|
|
self.client.post(url, {"is_vnc": "on"})
|
|
self.client.post(url, {"is_vnc": "on"}) # no change
|
|
self.client.post(reverse("accounts:user_instance_delete", args=[ui.pk]))
|
|
self.assertEqual(rows(), [
|
|
("VM vm owner bob: add", True),
|
|
("VM vm owner bob: change is_vnc", True),
|
|
("VM vm owner bob: remove", True),
|
|
])
|
|
self.assertEqual(set(Logs.objects.values_list("user", "host", "instance")), {("audit-admin", "c1", "vm")})
|
|
|
|
def test_otp_mail_without_an_email(self):
|
|
from smtplib import SMTPException
|
|
from unittest.mock import patch
|
|
|
|
from django_otp.plugins.otp_totp.models import TOTPDevice
|
|
|
|
url = reverse("accounts:admin_email_otp", args=[self.bob.pk])
|
|
with patch("accounts.views.send_email_with_otp") as send:
|
|
self.client.post(url)
|
|
send.assert_not_called()
|
|
self.assertFalse(TOTPDevice.objects.filter(user=self.bob).exists())
|
|
|
|
self.bob.email = "bob@example.com"
|
|
self.bob.save()
|
|
with patch("accounts.views.send_email_with_otp") as send:
|
|
self.client.post(url)
|
|
send.assert_called_once()
|
|
with patch("accounts.views.send_email_with_otp", side_effect=SMTPException("refused")), \
|
|
self.assertRaises(SMTPException):
|
|
self.client.post(url)
|
|
self.assertEqual(rows(), [
|
|
("User bob: mail OTP QR code: no email address", False),
|
|
("User bob: mail OTP QR code", True),
|
|
("User bob: mail OTP QR code: SMTPException", False),
|
|
])
|
|
|
|
def test_ssh_keys(self):
|
|
from accounts.models import UserSSHKey
|
|
|
|
self.client.post(reverse("accounts:ssh_key_create"), {"keyname": "laptop", "keypublic": SSH_KEY})
|
|
key = UserSSHKey.objects.get(keyname="laptop")
|
|
self.client.post(reverse("accounts:ssh_key_delete", args=[key.pk]))
|
|
self.assertEqual(rows(), [("SSH key laptop: add", True), ("SSH key laptop: delete", True)])
|
|
self.assertFalse(Logs.objects.filter(message__contains="AAAA").exists())
|