Skip to content

Commit b2d2a1a

Browse files
committed
Allow named disposables in OpenInVM config
1 parent 78bb7ba commit b2d2a1a

5 files changed

Lines changed: 123 additions & 4 deletions

File tree

qubes_config/global_config/disposables.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"""
2121
Disposables page handler
2222
"""
23+
2324
import subprocess
2425
from enum import Enum
2526

@@ -442,6 +443,7 @@ def __init__(
442443
policy_file_name="50-config-openinvm",
443444
prefix="openinvm",
444445
policy_manager=self.policy_manager,
446+
target_filter=self._permanent_dispvm_and_dispvm_template_filter,
445447
)
446448
self.openurl_handler = DispvmExceptionHandler(
447449
gtk_builder=gtk_builder,
@@ -450,6 +452,7 @@ def __init__(
450452
policy_file_name="50-config-openurl",
451453
prefix="url",
452454
policy_manager=self.policy_manager,
455+
target_filter=self._permanent_dispvm_and_dispvm_template_filter,
453456
)
454457
self.handlers: list[PageHandler | PropertyHandler] = [
455458
self.defdispvm_handler,
@@ -463,6 +466,16 @@ def __init__(
463466
def _default_dispvm_filter(vm) -> bool:
464467
return getattr(vm, "template_for_dispvms", False)
465468

469+
@staticmethod
470+
def _permanent_dispvm_and_dispvm_template_filter(vm) -> bool:
471+
if getattr(vm, "template_for_dispvms", False):
472+
return True
473+
474+
return (
475+
getattr(vm, "klass", None) == "DispVM"
476+
and getattr(vm, "auto_cleanup", False) is not True
477+
)
478+
466479
def get_unsaved(self) -> str:
467480
"""Get list of unsaved changes."""
468481
unsaved = []

qubes_config/global_config/policy_exceptions_handler.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ def __init__(
218218
prefix: str,
219219
policy_manager,
220220
policy_file_name: str,
221+
target_filter: Optional[Callable[[qubesadmin.vm.QubesVM], bool]] = None,
221222
):
222223
"""
223224
Handler for various dispvm-related exception lists
@@ -239,6 +240,7 @@ def __init__(
239240
self.service_name = service_name
240241
self.policy_manager = policy_manager
241242
self.policy_file_name = policy_file_name
243+
self.target_filter = target_filter
242244

243245
self.list_handler = PolicyExceptionsHandler(
244246
gtk_builder=gtk_builder,
@@ -282,6 +284,7 @@ def _get_row(self, rule: Rule, new: bool = False):
282284
}
283285
),
284286
is_new_row=new,
287+
filter_target=self.target_filter,
285288
)
286289

287290
def _new_rule(self) -> Rule:

qubes_config/global_config/rule_list_widgets.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
# You should have received a copy of the GNU Lesser General Public License along
1919
# with this program; if not, see <http://www.gnu.org/licenses/>.
2020
"""Widgets used by various list of policy rules."""
21+
2122
from typing import Optional, Dict, Callable, Any
2223

2324
from ..widgets.gtk_widgets import (
@@ -667,14 +668,17 @@ def __init__(
667668
qapp: qubesadmin.Qubes,
668669
verb_description: Optional[AbstractVerbDescription] = None,
669670
is_new_row: bool = False,
671+
filter_target: Optional[Callable] = None,
670672
):
671673
super().__init__(
672674
parent_handler=parent_handler,
673675
rule=rule,
674676
qapp=qapp,
675677
verb_description=verb_description,
676678
initial_verb=_("will"),
677-
filter_target=self._dvm_template_filter,
679+
filter_target=(
680+
filter_target or self._permanent_dispvm_and_dispvm_template_filter
681+
),
678682
is_new_row=is_new_row,
679683
source_categories=SOURCE_CATEGORIES,
680684
target_categories=DISPVM_CATEGORIES,
@@ -685,8 +689,14 @@ def __init__(
685689
self.target_widget.hide_selectors()
686690

687691
@staticmethod
688-
def _dvm_template_filter(vm: qubesadmin.vm.QubesVM):
689-
return getattr(vm, "template_for_dispvms", False)
692+
def _permanent_dispvm_and_dispvm_template_filter(vm: qubesadmin.vm.QubesVM):
693+
if getattr(vm, "template_for_dispvms", False):
694+
return True
695+
696+
return (
697+
getattr(vm, "klass", None) == "DispVM"
698+
and getattr(vm, "auto_cleanup", False) is not True
699+
)
690700

691701
def _hide_target_on_deny(self):
692702
if self.action_widget.get_selected() == "deny":

qubes_config/tests/test_disposables.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,43 @@ def test_dvm_list(real_builder, test_qapp, test_policy_manager):
202202
assert found_default
203203

204204

205+
def test_disposables_exception_rows_include_named_disposables(
206+
real_builder, test_qapp, test_policy_manager
207+
):
208+
test_qapp._qubes["volatile-disp"] = MockQube(
209+
name="volatile-disp",
210+
qapp=test_qapp,
211+
klass="DispVM",
212+
template="default-dvm",
213+
)
214+
test_qapp._qubes["volatile-disp"].properties["auto_cleanup"].value = True
215+
test_qapp.update_vm_calls()
216+
217+
disposables_handler = DisposablesHandler(
218+
test_qapp, test_policy_manager, real_builder
219+
)
220+
221+
default_dvm = test_qapp.domains["default-dvm"]
222+
named_dispvm = test_qapp.domains["test-disp"]
223+
volatile_dispvm = test_qapp.domains["volatile-disp"]
224+
regular_appvm = test_qapp.domains["test-vm"]
225+
226+
assert disposables_handler.defdispvm_handler.model.is_vm_available(default_dvm)
227+
assert not disposables_handler.defdispvm_handler.model.is_vm_available(named_dispvm)
228+
229+
for handler in (
230+
disposables_handler.open_in_dvm_handler,
231+
disposables_handler.openurl_handler,
232+
):
233+
handler.list_handler.add_button.clicked()
234+
row = next(row for row in handler.list_handler.current_rows if row.editing)
235+
236+
assert row.target_widget.model.is_vm_available(default_dvm)
237+
assert row.target_widget.model.is_vm_available(named_dispvm)
238+
assert not row.target_widget.model.is_vm_available(volatile_dispvm)
239+
assert not row.target_widget.model.is_vm_available(regular_appvm)
240+
241+
205242
def test_dispvm_change(real_builder, test_qapp, test_policy_manager):
206243
disposables_handler = DisposablesHandler(
207244
test_qapp, test_policy_manager, real_builder

qubes_config/tests/test_rule_list_widgets.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,21 @@
2121
# pylint: disable=missing-function-docstring
2222
# pylint: disable=missing-class-docstring
2323
from unittest.mock import Mock, patch
24+
from qubesadmin.tests.mock_app import MockQube
2425
from qrexec.policy.parser import Rule
2526
from ..global_config.policy_handler import PolicyHandler
26-
from ..global_config.policy_rules import RuleSimple, SimpleVerbDescription
27+
from ..global_config.policy_rules import (
28+
RuleDispVM,
29+
RuleSimple,
30+
SimpleVerbDescription,
31+
)
2732
from ..global_config.rule_list_widgets import (
2833
VMWidget,
2934
ActionWidget,
3035
RuleListBoxRow,
3136
NoActionListBoxRow,
3237
FilteredListBoxRow,
38+
DispvmRuleRow,
3339
)
3440

3541
import gi
@@ -298,6 +304,56 @@ def test_limited_selection_row(test_qapp):
298304
assert not rule_row.target_widget.model.is_vm_available(vm_vault)
299305

300306

307+
def test_dispvm_rule_row_includes_named_disposables(test_qapp):
308+
mock_handler = Mock(spec=PolicyHandler)
309+
mock_handler.verify_new_rule.return_value = None
310+
rule = RuleDispVM(
311+
Rule.from_line(
312+
None,
313+
"Service * test-blue @dispvm allow target=@dispvm",
314+
filepath=None,
315+
lineno=0,
316+
)
317+
)
318+
319+
rule_row = DispvmRuleRow(parent_handler=mock_handler, rule=rule, qapp=test_qapp)
320+
321+
default_dvm = test_qapp.domains["default-dvm"]
322+
named_dispvm = test_qapp.domains["test-disp"]
323+
regular_appvm = test_qapp.domains["test-vm"]
324+
325+
assert rule_row.target_widget.model.is_vm_available(default_dvm)
326+
assert rule_row.target_widget.model.is_vm_available(named_dispvm)
327+
assert not rule_row.target_widget.model.is_vm_available(regular_appvm)
328+
329+
330+
def test_dispvm_rule_row_excludes_volatile_disposables(test_qapp):
331+
mock_handler = Mock(spec=PolicyHandler)
332+
mock_handler.verify_new_rule.return_value = None
333+
test_qapp._qubes["volatile-disp"] = MockQube(
334+
name="volatile-disp",
335+
qapp=test_qapp,
336+
klass="DispVM",
337+
template="default-dvm",
338+
auto_cleanup=True,
339+
)
340+
test_qapp.update_vm_calls()
341+
rule = RuleDispVM(
342+
Rule.from_line(
343+
None,
344+
"Service * test-blue @dispvm allow target=@dispvm",
345+
filepath=None,
346+
lineno=0,
347+
)
348+
)
349+
350+
rule_row = DispvmRuleRow(parent_handler=mock_handler, rule=rule, qapp=test_qapp)
351+
352+
assert not rule_row.target_widget.model.is_vm_available(
353+
test_qapp.domains["volatile-disp"]
354+
)
355+
356+
301357
def test_rule_row_init_delete(test_qapp):
302358
mock_handler = Mock(spec=PolicyHandler)
303359
mock_handler.verify_new_rule.return_value = None

0 commit comments

Comments
 (0)