Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion qubesmanager/qube_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ def helpEvent(self, event, view, option, index):
QToolTip.showText(event.globalPos(),
self.tr(
"The qube is prohibited from starting\n"
"See `qvm-features` manual for more information"
"Prohibition rationale is available in qube settings "
"-> Advanced tab"
),
view
)
Expand Down
33 changes: 33 additions & 0 deletions qubesmanager/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,18 @@ def __init_advanced_tab__(self):
self.run_in_debug_mode.setVisible(False)
self.run_in_debug_mode.setEnabled(False)

rationale = utils.get_feature(self.vm, "prohibit-start", "")
if rationale:
self.prohibit_start_rationale.setText(rationale)
self.prohibit_start_rationale.setVisible(bool(rationale))
self.prohibit_start_rationale.textEdited.connect(
self.prohibit_start_rationale_edited
)
self.prohibit_start_checkbox.setChecked(bool(rationale))
self.prohibit_start_checkbox.clicked.connect(
self.prohibit_start_checked
)

utils.initialize_widget(
widget=self.allow_fullscreen,
choices=[
Expand All @@ -1041,6 +1053,16 @@ def __init_advanced_tab__(self):
'gui-allow-utf8-titles'))
self.allow_utf8_initial = self.allow_utf8.currentIndex()

def prohibit_start_checked(self, status):
self.prohibit_start_rationale.setVisible(bool(status))
self.prohibit_start_rationale.setFocus()

def prohibit_start_rationale_edited(self, text):
self.prohibit_start_checkbox.setChecked(bool(text))
self.prohibit_start_rationale.setStyleSheet(
"" if bool(text) else "#prohibit_start_rationale { color: red;}"
)

def enable_seamless(self):
try:
self.vm.run_service_for_stdio("qubes.SetGuiMode", input=b'SEAMLESS')
Expand Down Expand Up @@ -1146,6 +1168,17 @@ def __apply_advanced_tab__(self):
except qubesadmin.exc.QubesException as ex:
msg.append(str(ex))

rationale = self.vm.features.get("prohibit-start", "")
if (self.prohibit_start_checkbox.isChecked() != bool(rationale)) or (
bool(rationale)
and self.prohibit_start_rationale.text() != rationale
):
rationale = self.prohibit_start_rationale.text()
if bool(rationale) and self.prohibit_start_checkbox.isChecked():
self.vm.features["prohibit-start"] = rationale
else:
del self.vm.features["prohibit-start"]

if self.allow_fullscreen_initial !=\
self.allow_fullscreen.currentIndex():
try:
Expand Down
35 changes: 35 additions & 0 deletions qubesmanager/tests/test_vm_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,41 @@ def test_215_bootmode_appvm_nondefault(settings_fixture):
"Boot mode not changed"


@check_errors
@pytest.mark.parametrize("settings_fixture", TEST_VMS, indirect=True)
def test_213_prohibit_start(settings_fixture):
settings_window, page, vm_name = settings_fixture
vm = settings_window.qubesapp.domains[vm_name]

r = vm.features.get("prohibit-start", "")
assert settings_window.prohibit_start_checkbox.isChecked() == bool(r)
if r:
assert settings_window.prohibit_start_rationale.text() == r

settings_window.prohibit_start_checkbox.setChecked(not bool(r))

if bool(r):
settings_window.prohibit_start_rationale.setText("")
expected_call = (
vm.name,
"admin.vm.feature.Remove",
"prohibit-start",
None,
)
else:
settings_window.prohibit_start_rationale.setText("RATIONALE")
expected_call = (
vm.name,
"admin.vm.feature.Set",
"prohibit-start",
"RATIONALE".encode(),
)

assert expected_call not in settings_window.qubesapp.actual_calls
settings_window.qubesapp.expected_calls[expected_call] = b"0\x00"
settings_window.accept()


# FIREWALL TAB

@check_errors
Expand Down
25 changes: 23 additions & 2 deletions ui/settingsdlg.ui
Original file line number Diff line number Diff line change
Expand Up @@ -742,14 +742,35 @@ border-width: 1px;</string>
<property name="topMargin">
<number>15</number>
</property>
<item row="2" column="0">
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_prohibit_start">
<item>
<widget class="QCheckBox" name="prohibit_start_checkbox">
<property name="text">
<string>Prohibit start</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="prohibit_start_rationale">
<property name="text">
<string>Rationale for start prohibition...</string>
</property>
<property name="placeholderText">
<string>Start prohibition rationale is mandatory!</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="provides_network_checkbox">
<property name="text">
<string>Provides network</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QCheckBox" name="dvm_template_checkbox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Allows using this qube as a template for disposables. Disposables will inherit the qube's state (configuration, installed programs, etc.), but their state will not persist between restarts. &lt;/p&gt;&lt;p&gt;Setting this option will cause this qube to be listed as an option in the &amp;quot;Default disposable template&amp;quot; list for all other qubes.&lt;/p&gt;&lt;p&gt;Only AppVMs and StandaloneVMs could be turned in to Disposable Templates&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
Expand Down