Browse Source

Implement "blind mode" to avoid listing objects

This allows to perform actions on objects (VM, storage etc), without
listing them. This is useful when calling VM have minimal permissions
and only selected actions are allowed.

This means that app.domains['some-name'] will not raise KeyError, even
when domain do not exists. But performing actual action (like
vm.start()) will fail in that case.
Marek Marczykowski-Górecki 6 years ago
parent
commit
b28ddb6621
3 changed files with 31 additions and 4 deletions
  1. 8 3
      qubesadmin/app.py
  2. 1 1
      qubesadmin/base.py
  3. 22 0
      qubesadmin/tests/app.py

+ 8 - 3
qubesadmin/app.py

@@ -83,11 +83,14 @@ class VMCollection(object):
                 del self._vm_objects[name]
                 del self._vm_objects[name]
 
 
     def __getitem__(self, item):
     def __getitem__(self, item):
-        if item not in self:
+        if not self.app.blind_mode and item not in self:
             raise KeyError(item)
             raise KeyError(item)
         if item not in self._vm_objects:
         if item not in self._vm_objects:
-            cls = qubesadmin.utils.get_entry_point_one(VM_ENTRY_POINT,
-                self._vm_list[item]['class'])
+            if self.app.blind_mode:
+                cls = qubesadmin.vm.QubesVM
+            else:
+                cls = qubesadmin.utils.get_entry_point_one(VM_ENTRY_POINT,
+                    self._vm_list[item]['class'])
             self._vm_objects[item] = cls(self.app, item)
             self._vm_objects[item] = cls(self.app, item)
         return self._vm_objects[item]
         return self._vm_objects[item]
 
 
@@ -128,6 +131,8 @@ class QubesBase(qubesadmin.base.PropertyHolder):
     qubesd_connection_type = None
     qubesd_connection_type = None
     #: logger
     #: logger
     log = None
     log = None
+    #: do not check for object (VM, label etc) existence before really needed
+    blind_mode = False
 
 
     def __init__(self):
     def __init__(self):
         super(QubesBase, self).__init__(self, 'admin.property.', 'dom0')
         super(QubesBase, self).__init__(self, 'admin.property.', 'dom0')

+ 1 - 1
qubesadmin/base.py

@@ -292,7 +292,7 @@ class WrapperObjectsCollection(object):
                 del self._objects[name]
                 del self._objects[name]
 
 
     def __getitem__(self, item):
     def __getitem__(self, item):
-        if item not in self:
+        if not self.app.blind_mode and item not in self:
             raise KeyError(item)
             raise KeyError(item)
         if item not in self._objects:
         if item not in self._objects:
             self._objects[item] = self._object_class(self.app, item)
             self._objects[item] = self._object_class(self.app, item)

+ 22 - 0
qubesadmin/tests/app.py

@@ -99,6 +99,28 @@ class TC_00_VMCollection(qubesadmin.tests.QubesTestCase):
             set(['test-vm', 'test-vm2']))
             set(['test-vm', 'test-vm2']))
         self.assertAllCalled()
         self.assertAllCalled()
 
 
+    def test_007_getitem_blind_mode(self):
+        self.app.blind_mode = True
+        try:
+            vm = self.app.domains['test-vm']
+            self.assertEqual(vm.name, 'test-vm')
+        except KeyError:
+            self.fail('VM not found in collection')
+        self.assertAllCalled()
+
+        with self.assertNotRaises(KeyError):
+            vm = self.app.domains['test-non-existent']
+        self.assertAllCalled()
+
+    def test_008_in_blind_mode(self):
+        self.app.blind_mode = True
+        self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
+            b'0\x00test-vm class=AppVM state=Running\n'
+        self.assertIn('test-vm', self.app.domains)
+        self.assertAllCalled()
+
+        self.assertNotIn('test-non-existent', self.app.domains)
+        self.assertAllCalled()
 
 
 
 
 class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):
 class TC_10_QubesBase(qubesadmin.tests.QubesTestCase):