Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 3 additions & 2 deletions 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 Expand Up @@ -721,7 +722,7 @@ def filterAcceptsRow(self, sourceRow, sourceParent):
return super().filterAcceptsRow(sourceRow, sourceParent)

if self.window.show_running.isChecked() and \
vm.state['power'] != 'Halted':
not vm.state['power'] in ['Halted', 'Blocked']:
return super().filterAcceptsRow(sourceRow, sourceParent)
if self.window.show_halted.isChecked() and \
vm.state['power'] == 'Halted':
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