summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/feature-3534_preference-select-enabled-services1
-rw-r--r--src/leap/bitmask/config/leapsettings.py29
-rw-r--r--src/leap/bitmask/gui/mainwindow.py38
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py160
-rw-r--r--src/leap/bitmask/gui/ui/mainwindow.ui2
-rw-r--r--src/leap/bitmask/gui/ui/preferences.ui78
-rw-r--r--src/leap/bitmask/gui/wizard.py25
-rw-r--r--src/leap/bitmask/services/__init__.py35
8 files changed, 302 insertions, 66 deletions
diff --git a/changes/feature-3534_preference-select-enabled-services b/changes/feature-3534_preference-select-enabled-services
new file mode 100644
index 00000000..e3376877
--- /dev/null
+++ b/changes/feature-3534_preference-select-enabled-services
@@ -0,0 +1 @@
+ o Add preferences option to select the enabled services of a provider. Closes #3534.
diff --git a/src/leap/bitmask/config/leapsettings.py b/src/leap/bitmask/config/leapsettings.py
index 4929ab41..4604b807 100644
--- a/src/leap/bitmask/config/leapsettings.py
+++ b/src/leap/bitmask/config/leapsettings.py
@@ -75,7 +75,6 @@ class LeapSettings(object):
the config
:type standalone: bool
"""
-
settings_path = os.path.join(
get_path_prefix(standalone=standalone), "leap", self.CONFIG_NAME)
@@ -118,6 +117,26 @@ class LeapSettings(object):
leap_assert(windowstate, "We need a window state")
self._settings.setValue(self.WINDOWSTATE_KEY, windowstate)
+ def get_configured_providers(self):
+ """
+ Returns the configured providers based on the file structure in the
+ settings directory.
+
+ :rtype: list of str
+ """
+ # TODO: check which providers have a valid certificate among
+ # other things, not just the directories
+ providers = []
+ try:
+ providers_path = os.path.join(
+ self._path_prefix, "leap", "providers")
+ providers = os.listdir(providers_path)
+ except Exception as e:
+ logger.debug("Error listing providers, assume there are none. %r"
+ % (e,))
+
+ return providers
+
def get_enabled_services(self, provider):
"""
Returns a list of enabled services for the given provider
@@ -150,8 +169,12 @@ class LeapSettings(object):
leap_assert(len(provider) > 0, "We need a nonempty provider")
leap_assert_type(services, list)
- self._settings.setValue("%s/Services" % (provider,),
- services)
+ key = "{0}/Services".format(provider)
+ if not services:
+ # if there are no enabled services we don't need that key
+ self._settings.remove(key)
+ else:
+ self._settings.setValue(key, services)
def get_user(self):
"""
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index c832887a..34451928 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -288,17 +288,12 @@ class MainWindow(QtGui.QMainWindow):
################################# end Qt Signals connection ########
- # Enable the password change when soledad is ready
- self.soledad_ready.connect(
- partial(self.ui.btnPreferences.setEnabled, True))
-
init_platform()
self._wizard = None
self._wizard_firstrun = False
self._logger_window = None
- self._preferences_window = None
self._bypass_checks = bypass_checks
@@ -419,7 +414,11 @@ class MainWindow(QtGui.QMainWindow):
Displays the preferences window.
"""
- PreferencesWindow(self, self._srp_auth, self._soledad).show()
+ preferences_window = PreferencesWindow(
+ self, self._srp_auth, self._soledad,
+ self._settings, self._standalone)
+
+ preferences_window.show()
def _uncheck_logger_button(self):
"""
@@ -491,7 +490,8 @@ class MainWindow(QtGui.QMainWindow):
"""
# XXX: May be this can be divided into two methods?
- self._login_widget.set_providers(self._configured_providers())
+ providers = self._settings.get_configured_providers()
+ self._login_widget.set_providers(providers)
self._show_systray()
self.show()
if IS_MAC:
@@ -736,34 +736,14 @@ class MainWindow(QtGui.QMainWindow):
QtGui.QMainWindow.closeEvent(self, e)
- def _configured_providers(self):
- """
- Returns the available providers based on the file structure
-
- :rtype: list
- """
-
- # TODO: check which providers have a valid certificate among
- # other things, not just the directories
- providers = []
- try:
- providers = os.listdir(
- os.path.join(self._provider_config.get_path_prefix(),
- "leap",
- "providers"))
- except Exception as e:
- logger.debug("Error listing providers, assume there are none. %r"
- % (e,))
-
- return providers
-
def _first_run(self):
"""
Returns True if there are no configured providers. False otherwise
:rtype: bool
"""
- has_provider_on_disk = len(self._configured_providers()) != 0
+ providers = self._settings.get_configured_providers()
+ has_provider_on_disk = len(providers) != 0
is_proper_provider = self._settings.get_properprovider()
return not (has_provider_on_disk and is_proper_provider)
diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py
index a8220e86..05f616b0 100644
--- a/src/leap/bitmask/gui/preferenceswindow.py
+++ b/src/leap/bitmask/gui/preferenceswindow.py
@@ -18,15 +18,19 @@
"""
Preferences window
"""
+import os
import logging
from functools import partial
-from PySide import QtGui
+from PySide import QtCore, QtGui
from leap.bitmask.gui.ui_preferences import Ui_Preferences
from leap.soledad.client import NoStorageSecret
from leap.bitmask.crypto.srpauth import SRPAuthBadPassword
from leap.bitmask.util.password import basic_password_checks
+from leap.bitmask.services import get_supported
+from leap.bitmask.config.providerconfig import ProviderConfig
+from leap.bitmask.services import get_service_display_name
logger = logging.getLogger(__name__)
@@ -38,7 +42,7 @@ class PreferencesWindow(QtGui.QDialog):
WEAK_PASSWORDS = ("123456", "qweasd", "qwerty", "password")
- def __init__(self, parent, srp_auth, soledad):
+ def __init__(self, parent, srp_auth, soledad, leap_settings, standalone):
"""
:param parent: parent object of the PreferencesWindow.
:parent type: QWidget
@@ -46,19 +50,48 @@ class PreferencesWindow(QtGui.QDialog):
:type srp_auth: SRPAuth
:param soledad: Soledad object configured in the main app.
:type soledad: Soledad
+ :param standalone: If True, the application is running as standalone
+ and the preferences dialog should display some
+ messages according to this.
+ :type standalone: bool
"""
QtGui.QDialog.__init__(self, parent)
self._srp_auth = srp_auth
self._soledad = soledad
+ self._settings = leap_settings
+ self._standalone = standalone
# Load UI
self.ui = Ui_Preferences()
self.ui.setupUi(self)
self.ui.lblPasswordChangeStatus.setVisible(False)
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+
+ self._selected_services = set()
+ self._provider_config = ProviderConfig()
# Connections
self.ui.pbChangePassword.clicked.connect(self._change_password)
+ self.ui.cbProvidersServices.currentIndexChanged[unicode].connect(
+ self._populate_services)
+
+ parent.soledad_ready.connect(self._soledad_ready)
+
+ if not self._settings.get_configured_providers():
+ self.ui.gbEnabledServices.setEnabled(False)
+ else:
+ self._add_configured_providers()
+
+ def _soledad_ready(self):
+ """
+ SLOT
+ TRIGGERS:
+ parent.soledad_ready
+ It sets the soledad object as ready to use.
+ """
+ self._soledad_ready = True
+ self.ui.gbPasswordChange.setEnabled(True)
def _set_password_change_status(self, status, error=False, success=False):
"""
@@ -134,7 +167,7 @@ class PreferencesWindow(QtGui.QDialog):
self._set_password_change_status(
self.tr("Password changed successfully."), success=True)
- self._clear_inputs()
+ self._clear_password_inputs()
self._set_changing_password(False)
def _change_password_problem(self, failure):
@@ -156,10 +189,129 @@ class PreferencesWindow(QtGui.QDialog):
self._set_changing_password(False)
failure.trap(Exception)
- def _clear_inputs(self):
+ def _clear_password_inputs(self):
"""
Clear the contents of the inputs.
"""
self.ui.leCurrentPassword.setText("")
self.ui.leNewPassword.setText("")
self.ui.leNewPassword2.setText("")
+
+ def _set_providers_services_status(self, status, success=False):
+ """
+ Sets the status label for the password change.
+
+ :param status: status message to display, can be HTML
+ :type status: str
+ """
+ if success:
+ status = "<font color='green'><b>%s</b></font>" % (status,)
+
+ self.ui.lblProvidersServicesStatus.setVisible(True)
+ self.ui.lblProvidersServicesStatus.setText(status)
+
+ def _add_configured_providers(self):
+ """
+ Add the client's configured providers to the providers combo boxes.
+ """
+ self.ui.cbProvidersServices.clear()
+ self.ui.cbProvidersGateway.clear()
+ for provider in self._settings.get_configured_providers():
+ self.ui.cbProvidersServices.addItem(provider)
+ self.ui.cbProvidersGateway.addItem(provider)
+
+ def _service_selection_changed(self, service, state):
+ """
+ SLOT
+ TRIGGER: service_checkbox.stateChanged
+ Adds the service to the state if the state is checked, removes
+ it otherwise
+
+ :param service: service to handle
+ :type service: str
+ :param state: state of the checkbox
+ :type state: int
+ """
+ if state == QtCore.Qt.Checked:
+ self._selected_services = \
+ self._selected_services.union(set([service]))
+ else:
+ self._selected_services = \
+ self._selected_services.difference(set([service]))
+
+ # We hide the maybe-visible status label after a change
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+
+ def _populate_services(self, domain):
+ """
+ SLOT
+ TRIGGERS:
+ self.ui.cbProvidersServices.clicked
+
+ Loads the services that the provider provides into the UI for
+ the user to enable or disable.
+
+ :param domain: the domain of the provider to load services from.
+ :type domain: str
+ """
+ # We hide the maybe-visible status label after a change
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+
+ provider_config_path = os.path.join(
+ "leap", "providers", domain, "provider.json")
+
+ if not domain or not self._provider_config.load(provider_config_path):
+ return
+
+ # set the proper connection for the 'save' button
+ try:
+ self.ui.pbSaveServices.clicked.disconnect()
+ except RuntimeError:
+ pass # Signal was not connected
+
+ save_services = partial(self._save_enabled_services, domain)
+ self.ui.pbSaveServices.clicked.connect(save_services)
+
+ services = get_supported(self._provider_config.get_services())
+ services_conf = self._settings.get_enabled_services(domain)
+
+ # discard changes if other provider is selected
+ self._selected_services = set()
+
+ # from: http://stackoverflow.com/a/13103617/687989
+ # remove existing checkboxes
+ layout = self.ui.vlServices
+ for i in reversed(range(layout.count())):
+ layout.itemAt(i).widget().setParent(None)
+
+ # add one checkbox per service and set the current configured value
+ for service in services:
+ try:
+ checkbox = QtGui.QCheckBox(self)
+ service_label = get_service_display_name(
+ service, self._standalone)
+ checkbox.setText(service_label)
+
+ self.ui.vlServices.addWidget(checkbox)
+ checkbox.stateChanged.connect(
+ partial(self._service_selection_changed, service))
+
+ checkbox.setChecked(service in services_conf)
+ except ValueError:
+ logger.error("Something went wrong while trying to "
+ "load service %s" % (service,))
+
+ def _save_enabled_services(self, provider):
+ """
+ Saves the new settings to the configuration file.
+
+ :param provider: the provider config that we need to save.
+ :type provider: str
+ """
+ services = list(self._selected_services)
+ self._settings.set_enabled_services(provider, services)
+
+ msg = self.tr(
+ "Services settings for provider '{0}' saved.".format(provider))
+ logger.debug(msg)
+ self._set_providers_services_status(msg, success=True)
diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui
index 834a562e..17837642 100644
--- a/src/leap/bitmask/gui/ui/mainwindow.ui
+++ b/src/leap/bitmask/gui/ui/mainwindow.ui
@@ -217,7 +217,7 @@
<item>
<widget class="QPushButton" name="btnPreferences">
<property name="enabled">
- <bool>false</bool>
+ <bool>true</bool>
</property>
<property name="text">
<string>Preferences</string>
diff --git a/src/leap/bitmask/gui/ui/preferences.ui b/src/leap/bitmask/gui/ui/preferences.ui
index 8c63ccad..b59990b1 100644
--- a/src/leap/bitmask/gui/ui/preferences.ui
+++ b/src/leap/bitmask/gui/ui/preferences.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>451</width>
- <height>267</height>
+ <width>453</width>
+ <height>463</height>
</rect>
</property>
<property name="windowTitle">
@@ -17,9 +17,12 @@
<iconset resource="../../../../../data/resources/mainwindow.qrc">
<normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
<widget class="QGroupBox" name="gbPasswordChange">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="title">
<string>Password Change</string>
</property>
@@ -98,7 +101,64 @@
</layout>
</widget>
</item>
- <item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="gbEnabledServices">
+ <property name="title">
+ <string>Enabled services</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="3" column="1">
+ <widget class="QPushButton" name="pbSaveServices">
+ <property name="text">
+ <string>Save this provider settings</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QGroupBox" name="gbServices">
+ <property name="title">
+ <string>Services</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="vlServices"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cbProvidersServices">
+ <item>
+ <property name="text">
+ <string>&lt;Select provider&gt;</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Select provider:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="lblProvidersServicesStatus">
+ <property name="text">
+ <string>&lt; Providers Services Status &gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
<widget class="QGroupBox" name="gbGatewaySelector">
<property name="enabled">
<bool>false</bool>
@@ -116,12 +176,12 @@
<string>&amp;Select provider:</string>
</property>
<property name="buddy">
- <cstring>cbProviders</cstring>
+ <cstring>cbProvidersGateway</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
- <widget class="QComboBox" name="cbProviders">
+ <widget class="QComboBox" name="cbProvidersGateway">
<item>
<property name="text">
<string>&lt;Select provider&gt;</string>
@@ -137,7 +197,7 @@
</widget>
</item>
<item row="1" column="1">
- <widget class="QComboBox" name="comboBox">
+ <widget class="QComboBox" name="cbGateways">
<item>
<property name="text">
<string>Automatic</string>
@@ -148,7 +208,7 @@
</layout>
</widget>
</item>
- <item>
+ <item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index e3f0085b..ac0f032f 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -34,7 +34,7 @@ from leap.bitmask.util.request_helpers import get_content
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.password import basic_password_checks
from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services import get_supported
+from leap.bitmask.services import get_service_display_name, get_supported
from ui_wizard import Ui_Wizard
@@ -84,23 +84,6 @@ class Wizard(QtGui.QWizard):
self.ERROR_ICON = QtGui.QPixmap(":/images/Dialog-error.png")
self.OK_ICON = QtGui.QPixmap(":/images/Dialog-accept.png")
- # Correspondence for services and their name to display
- EIP_LABEL = self.tr("Encrypted Internet")
- MX_LABEL = self.tr("Encrypted Mail")
-
- if self._is_need_eip_password_warning():
- EIP_LABEL += " " + self.tr(
- "(will need admin password to start)")
-
- self.SERVICE_DISPLAY = [
- EIP_LABEL,
- MX_LABEL
- ]
- self.SERVICE_CONFIG = [
- "openvpn",
- "mx"
- ]
-
self._selected_services = set()
self._shown_services = set()
@@ -507,8 +490,10 @@ class Wizard(QtGui.QWizard):
try:
if service not in self._shown_services:
checkbox = QtGui.QCheckBox(self)
- service_index = self.SERVICE_CONFIG.index(service)
- checkbox.setText(self.SERVICE_DISPLAY[service_index])
+ service_label = get_service_display_name(
+ service, self.standalone)
+ checkbox.setText(service_label)
+
self.ui.serviceListLayout.addWidget(checkbox)
checkbox.stateChanged.connect(
partial(self._service_selection_changed, service))
diff --git a/src/leap/bitmask/services/__init__.py b/src/leap/bitmask/services/__init__.py
index 253359cd..924ca547 100644
--- a/src/leap/bitmask/services/__init__.py
+++ b/src/leap/bitmask/services/__init__.py
@@ -17,9 +17,44 @@
"""
Services module.
"""
+from PySide import QtCore
+from leap.bitmask.util.privilege_policies import is_missing_policy_permissions
+
DEPLOYED = ["openvpn", "mx"]
+def get_service_display_name(service, standalone=False):
+ """
+ Returns the name to display of the given service.
+
+ :param service: the 'machine' service name
+ :type service: str
+ :param standalone: True if the app is running in a standalone mode, used
+ to display messages according that.
+ :type standalone: bool
+
+ :rtype: str
+ """
+ # qt translator method helper
+ _tr = QtCore.QObject().tr
+
+ # Correspondence for services and their name to display
+ EIP_LABEL = _tr("Encrypted Internet")
+ MX_LABEL = _tr("Encrypted Mail")
+
+ service_display = [EIP_LABEL, MX_LABEL]
+ service_config = ["openvpn", "mx"]
+
+ # If we need to add a warning about eip needing
+ # administrative permissions to start. That can be either
+ # because we are running in standalone mode, or because we could
+ # not find the needed privilege escalation mechanisms being operative.
+ if standalone or is_missing_policy_permissions():
+ EIP_LABEL += " " + _tr("(will need admin password to start)")
+
+ return service_display[service_config.index(service)]
+
+
def get_supported(services):
"""
Returns a list of the available services.