mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-10 12:16:49 +00:00
Merge branch 'fix/wave0-authorization-matrix' into wave0-integration
# Conflicts: # datasource/views.py
This commit is contained in:
commit
51db12acfe
12 changed files with 390 additions and 75 deletions
74
instances/test_console_rule.py
Normal file
74
instances/test_console_rule.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
"""
|
||||
ROADMAP Wave 0 item 5 (S-09): one rule decides who may open a VM's console
|
||||
(noVNC page, novncd, the virt-viewer .vv file, the VDI URL): a superuser or
|
||||
an owner of the VM. The global view_instances permission is read-only.
|
||||
"""
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from accounts.models import UserInstance
|
||||
from computes.models import Compute
|
||||
from instances.models import Instance
|
||||
from instances.utils import can_open_console
|
||||
|
||||
|
||||
class CanOpenConsoleTestCase(TestCase):
|
||||
def setUp(self):
|
||||
User = get_user_model()
|
||||
self.superuser = User.objects.create_superuser(
|
||||
username="cons_super", email="cons_super@example.com", password="x"
|
||||
)
|
||||
self.owner = User.objects.create_user(username="cons_owner", password="x")
|
||||
self.inactive_owner = User.objects.create_user(
|
||||
username="cons_inactive", password="x", is_active=False
|
||||
)
|
||||
self.viewer = User.objects.create_user(username="cons_viewer", password="x")
|
||||
self.viewer.user_permissions.add(Permission.objects.get(codename="view_instances"))
|
||||
self.stranger = User.objects.create_user(username="cons_stranger", password="x")
|
||||
|
||||
self.compute = Compute.objects.create(
|
||||
name="cons-compute", hostname="127.0.0.1:1", login="root", password="", type=1
|
||||
)
|
||||
self.instance = Instance.objects.create(
|
||||
compute=self.compute,
|
||||
name="cons-vm",
|
||||
uuid="77777777-8888-9999-aaaa-bbbbbbbbbbbb",
|
||||
)
|
||||
UserInstance.objects.create(instance=self.instance, user=self.owner)
|
||||
UserInstance.objects.create(instance=self.instance, user=self.inactive_owner)
|
||||
|
||||
def test_superuser_and_owner_may_open_console(self):
|
||||
self.assertTrue(can_open_console(self.superuser, self.instance))
|
||||
self.assertTrue(can_open_console(self.owner, self.instance))
|
||||
|
||||
def test_view_instances_stranger_and_inactive_owner_may_not(self):
|
||||
self.assertFalse(can_open_console(self.viewer, self.instance))
|
||||
self.assertFalse(can_open_console(self.stranger, self.instance))
|
||||
self.assertFalse(can_open_console(self.inactive_owner, self.instance))
|
||||
|
||||
def test_owner_can_download_vv_file(self):
|
||||
self.client.force_login(self.owner)
|
||||
with patch("instances.views.wvmInstances") as mock_conn_cls:
|
||||
conn = mock_conn_cls.return_value
|
||||
conn.graphics_type.return_value = "vnc"
|
||||
conn.graphics_listen.return_value = "127.0.0.1"
|
||||
conn.graphics_port.return_value = "5900"
|
||||
conn.domain_name.return_value = "cons-vm"
|
||||
conn.graphics_passwd.return_value = "secret"
|
||||
res = self.client.get(reverse("instances:getvvfile", args=[self.instance.id]))
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertIn(b"[virt-viewer]", res.content)
|
||||
|
||||
def test_owner_can_get_vdi_url(self):
|
||||
self.client.force_login(self.owner)
|
||||
with patch("datasource.views.wvmInstance", MagicMock()), patch(
|
||||
"datasource.views.get_hostname_by_ip", return_value="host"
|
||||
):
|
||||
res = self.client.get(
|
||||
reverse("vdi_url", args=[self.compute.id, self.instance.name])
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
|
@ -805,13 +805,6 @@ class InstancesTestCase(TestCase):
|
|||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_check_instance(self):
|
||||
response = self.client.get(
|
||||
reverse("instances:check_instance", args=["test-vm"])
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(response.content, {"vname": "test-vm", "exists": True})
|
||||
|
||||
def test_start_template(self):
|
||||
# starting templates must fail
|
||||
self.assertEqual(self.instance.status, 5)
|
||||
|
|
|
|||
|
|
@ -82,6 +82,5 @@ urlpatterns = [
|
|||
),
|
||||
path("guess_clone_name/", views.guess_clone_name, name="guess_clone_name"),
|
||||
path("random_mac_address/", views.random_mac_address, name="random_mac_address"),
|
||||
path("check_instance/<vname>/", views.check_instance, name="check_instance"),
|
||||
path("<int:pk>/sshkeys/", views.sshkeys, name="sshkeys"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,20 @@ from vrtManager.instance import wvmInstance, wvmInstances
|
|||
from .models import Instance
|
||||
|
||||
|
||||
def can_open_console(user, instance):
|
||||
"""
|
||||
The single rule for console access (noVNC page, novncd, the virt-viewer
|
||||
.vv file and the VDI URL): an active superuser or an active owner of the
|
||||
VM. The global view_instances permission is read-only and does not grant
|
||||
an interactive console (ROADMAP S-09).
|
||||
"""
|
||||
if not user.is_active:
|
||||
return False
|
||||
if user.is_superuser:
|
||||
return True
|
||||
return UserInstance.objects.filter(instance=instance, user=user).exists()
|
||||
|
||||
|
||||
def get_clone_free_names(size=10):
|
||||
prefix = app_settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
||||
free_names = []
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ def osinfo(request, pk):
|
|||
return JsonResponse(results)
|
||||
|
||||
|
||||
@superuser_only
|
||||
def guess_mac_address(request, vname):
|
||||
data = {"vname": vname}
|
||||
mac = utils.get_dhcp_mac_address(vname)
|
||||
|
|
@ -264,12 +265,14 @@ def guess_mac_address(request, vname):
|
|||
return JsonResponse(data)
|
||||
|
||||
|
||||
@superuser_only
|
||||
def random_mac_address(request):
|
||||
data = dict()
|
||||
data["mac"] = utils.get_random_mac_address()
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@superuser_only
|
||||
def guess_clone_name(request):
|
||||
dhcp_file = "/srv/webvirtcloud/dhcpd.conf"
|
||||
prefix = app_settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
||||
|
|
@ -288,14 +291,6 @@ def guess_clone_name(request):
|
|||
return JsonResponse({})
|
||||
|
||||
|
||||
def check_instance(request, vname):
|
||||
instance = Instance.objects.filter(name=vname)
|
||||
data = {"vname": vname, "exists": False}
|
||||
if instance:
|
||||
data["exists"] = True
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
def sshkeys(request, pk):
|
||||
"""
|
||||
:param request:
|
||||
|
|
@ -1829,6 +1824,9 @@ def change_options(request, pk):
|
|||
|
||||
def getvvfile(request, pk):
|
||||
instance = get_instance(request.user, pk)
|
||||
# The .vv file contains the VNC password: same rule as the console.
|
||||
if not utils.can_open_console(request.user, instance):
|
||||
raise PermissionDenied
|
||||
conn = wvmInstances(
|
||||
instance.compute.hostname,
|
||||
instance.compute.login,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue