diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/bitmask/config/leapsettings.py | 29 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 38 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 160 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/mainwindow.ui | 2 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/preferences.ui | 78 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/wizard.py | 25 | ||||
| -rw-r--r-- | src/leap/bitmask/services/__init__.py | 35 | 
7 files changed, 301 insertions, 66 deletions
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><Select provider></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>< Providers Services Status ></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>&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><Select provider></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.  | 
