class LeapSettingsTest(BaseLeapTest):
    """Tests for LeapSettings"""

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_get_configured_providers(self):
        """
        Test that the config file IS NOT stored under the CWD.
        """
        self._leapsettings = LeapSettings()
        with mock.patch('os.listdir') as os_listdir:
            # use this method only to spy where LeapSettings is looking for
            self._leapsettings.get_configured_providers()
            args, kwargs = os_listdir.call_args
            config_dir = args[0]
            self.assertFalse(config_dir.startswith(os.getcwd()))
            self.assertFalse(config_dir.endswith('config'))

    def test_get_configured_providers_in_bundle(self):
        """
        Test that the config file IS stored under the CWD.
        """
        self._leapsettings = LeapSettings(standalone=True)
        with mock.patch('os.listdir') as os_listdir:
            # use this method only to spy where LeapSettings is looking for
            self._leapsettings.get_configured_providers()
            args, kwargs = os_listdir.call_args
            config_dir = args[0]
            self.assertTrue(config_dir.startswith(os.getcwd()))
            self.assertFalse(config_dir.endswith('config'))
예제 #2
0
    def _load_configured_providers(self):
        """
        Loads the configured providers into the wizard providers combo box.
        """
        ls = LeapSettings()
        providers = ls.get_configured_providers()
        if not providers:
            self.ui.rbExistingProvider.setEnabled(False)
            self.ui.label_8.setEnabled(False)  # 'https://' label
            self.ui.cbProviders.setEnabled(False)
            return

        pinned = []
        user_added = []

        # separate pinned providers from user added ones
        for p in providers:
            if ls.is_pinned_provider(p):
                pinned.append(p)
            else:
                user_added.append(p)

        if user_added:
            self.ui.cbProviders.addItems(user_added)

        if user_added and pinned:
            self.ui.cbProviders.addItem('---')

        if pinned:
            random.shuffle(pinned)  # don't prioritize alphabetically
            self.ui.cbProviders.addItems(pinned)

        # We have configured providers, so by default we select the
        # 'Use existing provider' option.
        self.ui.rbExistingProvider.setChecked(True)
예제 #3
0
class LeapSettingsTest(BaseLeapTest):
    """Tests for LeapSettings"""

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_get_configured_providers(self):
        """
        Test that the config file IS NOT stored under the CWD.
        """
        flags.STANDALONE = False
        self._leapsettings = LeapSettings()
        with mock.patch('os.listdir') as os_listdir:
            # use this method only to spy where LeapSettings is looking for
            self._leapsettings.get_configured_providers()
            args, kwargs = os_listdir.call_args
            config_dir = args[0]
            self.assertFalse(config_dir.startswith(os.getcwd()))
            self.assertFalse(config_dir.endswith('config'))

    def test_get_configured_providers_in_bundle(self):
        """
        Test that the config file IS stored under the CWD.
        """
        flags.STANDALONE = True
        self._leapsettings = LeapSettings()
        with mock.patch('os.listdir') as os_listdir:
            # use this method only to spy where LeapSettings is looking for
            self._leapsettings.get_configured_providers()
            args, kwargs = os_listdir.call_args
            config_dir = args[0]
            self.assertTrue(config_dir.startswith(os.getcwd()))
            self.assertFalse(config_dir.endswith('config'))
예제 #4
0
    def _load_configured_providers_with_pinned(self, pinned):
        """
        Once we have the pinned providers from the backend, we
        continue setting everything up

        :param pinned: list of pinned providers
        :type pinned: list of str
        """
        ls = LeapSettings()
        providers = ls.get_configured_providers()
        if not providers and not pinned:
            self.ui.rbExistingProvider.setEnabled(False)
            self.ui.label_8.setEnabled(False)  # 'https://' label
            self.ui.cbProviders.setEnabled(False)
            return

        user_added = []

        # separate pinned providers from user added ones
        for p in providers:
            if p not in pinned:
                user_added.append(p)

        if user_added:
            self.ui.cbProviders.addItems(user_added)

        if user_added and pinned:
            self.ui.cbProviders.addItem('---')

        if pinned:
            random.shuffle(pinned)  # don't prioritize alphabetically
            self.ui.cbProviders.addItems(pinned)

        # We have configured providers, so by default we select the
        # 'Use existing provider' option.
        self.ui.rbExistingProvider.setChecked(True)

        # We need to set it as complete explicitly
        self.page(self.INTRO_PAGE).set_completed()
예제 #5
0
    def _load_configured_providers_with_pinned(self, pinned):
        """
        Once we have the pinned providers from the backend, we
        continue setting everything up

        :param pinned: list of pinned providers
        :type pinned: list of str


        How the combobox items are arranged:
        -----------------------------------

        First run:

            demo.bitmask.net
            --
            pinned2.org
            pinned1.org
            pinned3.org

        After some usage:

            added-by-user.org
            pinned-but-then-used.org
            ---
            demo.bitmask.net
            pinned1.org
            pinned3.org
            pinned2.org

        In other words:
            * There are two sections.
            * Section one consists of all the providers that the user has used.
              If this is empty, than use demo.bitmask.net for this section.
              This list is sorted alphabetically.
            * Section two consists of all the pinned or 'pre seeded' providers,
              minus any providers that are now in section one. This last list
              is in random order.
        """
        ls = LeapSettings()
        user_added = ls.get_configured_providers()
        if not user_added and not pinned:
            self.ui.rbExistingProvider.setEnabled(False)
            self.ui.label_8.setEnabled(False)  # 'https://' label
            self.ui.cbProviders.setEnabled(False)
            return

        user_added.sort()

        if not user_added:
            user_added = [pinned.pop(0)]

        # separate unused pinned providers from user added ones
        for p in user_added:
            if p in pinned:
                pinned.remove(p)

        if user_added:
            self.ui.cbProviders.addItems(user_added)

        if user_added and pinned:
            self.ui.cbProviders.addItem('---')

        if pinned:
            random.shuffle(pinned)  # don't prioritize alphabetically
            self.ui.cbProviders.addItems(pinned)

        # We have configured providers, so by default we select the
        # 'Use existing provider' option.
        self.ui.rbExistingProvider.setChecked(True)

        # We need to set it as complete explicitly
        self.page(self.INTRO_PAGE).set_completed()
예제 #6
0
class PreferencesWindow(QtGui.QDialog):
    """
    Window that displays the preferences.
    """
    def __init__(self, parent, srp_auth):
        """
        :param parent: parent object of the PreferencesWindow.
        :parent type: QWidget
        :param srp_auth: SRPAuth object configured in the main app.
        :type srp_auth: SRPAuth
        """
        QtGui.QDialog.__init__(self, parent)
        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")

        self._srp_auth = srp_auth
        self._settings = LeapSettings()
        self._soledad = None

        # Load UI
        self.ui = Ui_Preferences()
        self.ui.setupUi(self)
        self.ui.lblPasswordChangeStatus.setVisible(False)
        self.ui.lblProvidersServicesStatus.setVisible(False)
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        self._selected_services = set()

        # Connections
        self.ui.pbChangePassword.clicked.connect(self._change_password)
        self.ui.cbProvidersServices.currentIndexChanged[unicode].connect(
            self._populate_services)
        self.ui.cbProvidersGateway.currentIndexChanged[unicode].connect(
            self._populate_gateways)

        self.ui.cbGateways.currentIndexChanged[unicode].connect(
            lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))

        if not self._settings.get_configured_providers():
            self.ui.gbEnabledServices.setEnabled(False)
        else:
            self._add_configured_providers()

    def set_soledad_ready(self, soledad):
        """
        SLOT
        TRIGGERS:
            parent.soledad_ready

        It sets the soledad object as ready to use.

        :param soledad: Soledad object configured in the main app.
        :type soledad: Soledad
        """
        self._soledad = soledad
        self.ui.gbPasswordChange.setEnabled(True)

    def _set_password_change_status(self, status, error=False, success=False):
        """
        Sets the status label for the password change.

        :param status: status message to display, can be HTML
        :type status: str
        """
        if error:
            status = "<font color='red'><b>%s</b></font>" % (status,)
        elif success:
            status = "<font color='green'><b>%s</b></font>" % (status,)

        self.ui.lblPasswordChangeStatus.setVisible(True)
        self.ui.lblPasswordChangeStatus.setText(status)

    def _set_changing_password(self, disable):
        """
        Enables or disables the widgets in the password change group box.

        :param disable: True if the widgets should be disabled and
                        it displays the status label that shows that is
                        changing the password.
                        False if they should be enabled.
        :type disable: bool
        """
        if disable:
            self._set_password_change_status(self.tr("Changing password..."))

        self.ui.leCurrentPassword.setEnabled(not disable)
        self.ui.leNewPassword.setEnabled(not disable)
        self.ui.leNewPassword2.setEnabled(not disable)
        self.ui.pbChangePassword.setEnabled(not disable)

    def _change_password(self):
        """
        SLOT
        TRIGGERS:
            self.ui.pbChangePassword.clicked

        Changes the user's password if the inputboxes are correctly filled.
        """
        username = self._srp_auth.get_username()
        current_password = self.ui.leCurrentPassword.text()
        new_password = self.ui.leNewPassword.text()
        new_password2 = self.ui.leNewPassword2.text()

        ok, msg = basic_password_checks(username, new_password, new_password2)

        if not ok:
            self._set_changing_password(False)
            self._set_password_change_status(msg, error=True)
            self.ui.leNewPassword.setFocus()
            return

        self._set_changing_password(True)
        d = self._srp_auth.change_password(current_password, new_password)
        d.addCallback(partial(self._change_password_success, new_password))
        d.addErrback(self._change_password_problem)

    def _change_password_success(self, new_password, _):
        """
        Callback used to display a successfully performed action.

        :param new_password: the new password for the user.
        :type new_password: str.
        :param _: the returned data from self._srp_auth.change_password
                  Ignored
        """
        logger.debug("SRP password changed successfully.")
        try:
            self._soledad.change_passphrase(str(new_password))
            logger.debug("Soledad password changed successfully.")
        except NoStorageSecret:
            logger.debug(
                "No storage secret for password change in Soledad.")

        self._set_password_change_status(
            self.tr("Password changed successfully."), success=True)
        self._clear_password_inputs()
        self._set_changing_password(False)

    def _change_password_problem(self, failure):
        """
        Errback called if there was a problem with the deferred.
        Also is used to display an error message.

        :param failure: the cause of the method failed.
        :type failure: twisted.python.Failure
        """
        logger.error("Error changing password: %s", (failure, ))
        problem = self.tr("There was a problem changing the password.")

        if failure.check(SRPAuthBadPassword):
            problem = self.tr("You did not enter a correct current password.")

        self._set_password_change_status(problem, error=True)

        self._set_changing_password(False)
        failure.trap(Exception)

    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
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        """
        if success:
            status = "<font color='green'><b>%s</b></font>" % (status,)

        self.ui.lblProvidersServicesStatus.setVisible(True)
        self.ui.lblProvidersServicesStatus.setText(status)

    def _set_providers_gateway_status(self, status, success=False,
                                      error=False):
        """
        Sets the status label for the gateway change.

        :param status: status message to display, can be HTML
        :type status: str
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        :param error: is set to True if we should display the
                        message as red
        :type error: bool
        """
        if success:
            status = "<font color='green'><b>%s</b></font>" % (status,)
        elif error:
            status = "<font color='red'><b>%s</b></font>" % (status,)

        self.ui.lblProvidersGatewayStatus.setVisible(True)
        self.ui.lblProvidersGatewayStatus.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.currentIndexChanged[unicode]

        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)

        if not domain:
            return

        provider_config = self._get_provider_config(domain)
        if provider_config is None:
            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(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)
                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):
        """
        SLOT
        TRIGGERS:
            self.ui.pbSaveServices.clicked

        Saves the new enabled services 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)

    def _get_provider_config(self, domain):
        """
        Helper to return a valid Provider Config from the domain name.

        :param domain: the domain name of the provider.
        :type domain: str

        :rtype: ProviderConfig or None if there is a problem loading the config
        """
        provider_config = ProviderConfig()
        provider_config_path = os.path.join(
            "leap", "providers", domain, "provider.json")

        if not provider_config.load(provider_config_path):
            provider_config = None

        return provider_config

    def _save_selected_gateway(self, provider):
        """
        SLOT
        TRIGGERS:
            self.ui.pbSaveGateway.clicked

        Saves the new gateway setting to the configuration file.

        :param provider: the provider config that we need to save.
        :type provider: str
        """
        gateway = self.ui.cbGateways.currentText()

        if gateway == self.AUTOMATIC_GATEWAY_LABEL:
            gateway = self._settings.GATEWAY_AUTOMATIC
        else:
            idx = self.ui.cbGateways.currentIndex()
            gateway = self.ui.cbGateways.itemData(idx)

        self._settings.set_selected_gateway(provider, gateway)

        msg = self.tr(
            "Gateway settings for provider '{0}' saved.".format(provider))
        logger.debug(msg)
        self._set_providers_gateway_status(msg, success=True)

    def _populate_gateways(self, domain):
        """
        SLOT
        TRIGGERS:
            self.ui.cbProvidersGateway.currentIndexChanged[unicode]

        Loads the gateways that the provider provides into the UI for
        the user to select.

        :param domain: the domain of the provider to load gateways from.
        :type domain: str
        """
        # We hide the maybe-visible status label after a change
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        if not domain:
            return

        try:
            # disconnect prevoiusly connected save method
            self.ui.pbSaveGateway.clicked.disconnect()
        except RuntimeError:
            pass  # Signal was not connected

        # set the proper connection for the 'save' button
        save_gateway = partial(self._save_selected_gateway, domain)
        self.ui.pbSaveGateway.clicked.connect(save_gateway)

        eip_config = EIPConfig()
        provider_config = self._get_provider_config(domain)

        eip_config_path = os.path.join("leap", "providers",
                                       domain, "eip-service.json")
        api_version = provider_config.get_api_version()
        eip_config.set_api_version(api_version)
        eip_loaded = eip_config.load(eip_config_path)

        if not eip_loaded or provider_config is None:
            self._set_providers_gateway_status(
                self.tr("There was a problem with configuration files."),
                error=True)
            return

        gateways = VPNGatewaySelector(eip_config).get_gateways_list()
        logger.debug(gateways)

        self.ui.cbGateways.clear()
        self.ui.cbGateways.addItem(self.AUTOMATIC_GATEWAY_LABEL)

        # Add the available gateways and
        # select the one stored in configuration file.
        selected_gateway = self._settings.get_selected_gateway(domain)
        index = 0
        for idx, (gw_name, gw_ip) in enumerate(gateways):
            gateway = "{0} ({1})".format(gw_name, gw_ip)
            self.ui.cbGateways.addItem(gateway, gw_ip)
            if gw_ip == selected_gateway:
                index = idx + 1

        self.ui.cbGateways.setCurrentIndex(index)
예제 #7
0
class PreferencesWindow(QtGui.QDialog):
    """
    Window that displays the preferences.
    """
    preferences_saved = QtCore.Signal()

    def __init__(self, parent, username, domain, backend, soledad_started, mx,
                 leap_signaler):
        """
        :param parent: parent object of the PreferencesWindow.
        :parent type: QWidget
        :param username: the user set in the login widget
        :type username: unicode
        :param domain: the selected domain in the login widget
        :type domain: unicode
        :param backend: Backend being used
        :type backend: Backend
        :param soledad_started: whether soledad has started or not
        :type soledad_started: bool
        :param mx: whether the current provider provides mx or not.
        :type mx: bool
        """
        QtGui.QDialog.__init__(self, parent)
        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")

        self._username = username
        self._domain = domain
        self._leap_signaler = leap_signaler
        self._backend = backend
        self._soledad_started = soledad_started
        self._mx_provided = mx

        self._settings = LeapSettings()
        self._backend_connect()

        # Load UI
        self.ui = Ui_Preferences()
        self.ui.setupUi(self)
        self.ui.lblPasswordChangeStatus.setVisible(False)
        self.ui.lblProvidersServicesStatus.setVisible(False)

        self._selected_services = set()

        # Connections
        self.ui.pbChangePassword.clicked.connect(self._change_password)
        self.ui.cbProvidersServices.currentIndexChanged[unicode].connect(
            self._populate_services)

        if not self._settings.get_configured_providers():
            self.ui.gbEnabledServices.setEnabled(False)
        else:
            self._add_configured_providers()

        if self._username is None:
            self._not_logged_in()
        else:
            self.ui.gbPasswordChange.setEnabled(True)
            if self._mx_provided:
                self._provides_mx()

        self._select_provider_by_name(domain)

    def _not_logged_in(self):
        """
        Actions to perform if the user is not logged in.
        """
        msg = self.tr(
            "In order to change your password you need to be logged in.")
        self._set_password_change_status(msg)
        self.ui.gbPasswordChange.setEnabled(False)

    def _provides_mx(self):
        """
        Actions to perform if the provider provides MX.
        """
        pw_enabled = True
        enabled_services = self._settings.get_enabled_services(self._domain)
        mx_name = get_service_display_name(MX_SERVICE)

        if MX_SERVICE not in enabled_services:
            msg = self.tr("You need to enable {0} in order to change "
                          "the password.".format(mx_name))
            self._set_password_change_status(msg, error=True)
            pw_enabled = False
        else:
            # check if Soledad is bootstrapped
            if not self._soledad_started:
                msg = self.tr(
                    "You need to wait until {0} is ready in "
                    "order to change the password.".format(mx_name))
                self._set_password_change_status(msg)
                pw_enabled = False

        self.ui.gbPasswordChange.setEnabled(pw_enabled)

    @QtCore.Slot()
    def set_soledad_ready(self):
        """
        TRIGGERS:
            parent.soledad_ready

        It notifies when the soledad object as ready to use.
        """
        self.ui.lblPasswordChangeStatus.setVisible(False)
        self.ui.gbPasswordChange.setEnabled(True)

    def _set_password_change_status(self, status, error=False, success=False):
        """
        Sets the status label for the password change.

        :param status: status message to display, can be HTML
        :type status: str
        """
        if error:
            status = "<font color='red'><b>%s</b></font>" % (status,)
        elif success:
            status = "<font color='green'><b>%s</b></font>" % (status,)

        if not self.ui.gbPasswordChange.isEnabled():
            status = "<font color='black'>%s</font>" % (status,)

        self.ui.lblPasswordChangeStatus.setVisible(True)
        self.ui.lblPasswordChangeStatus.setText(status)

    def _set_changing_password(self, disable):
        """
        Enables or disables the widgets in the password change group box.

        :param disable: True if the widgets should be disabled and
                        it displays the status label that shows that is
                        changing the password.
                        False if they should be enabled.
        :type disable: bool
        """
        if disable:
            self._set_password_change_status(self.tr("Changing password..."))

        self.ui.leCurrentPassword.setEnabled(not disable)
        self.ui.leNewPassword.setEnabled(not disable)
        self.ui.leNewPassword2.setEnabled(not disable)
        self.ui.pbChangePassword.setEnabled(not disable)

    @QtCore.Slot()
    def _change_password(self):
        """
        TRIGGERS:
            self.ui.pbChangePassword.clicked

        Changes the user's password if the inputboxes are correctly filled.
        """
        username = self._username
        current_password = self.ui.leCurrentPassword.text()
        new_password = self.ui.leNewPassword.text()
        new_password2 = self.ui.leNewPassword2.text()

        ok, msg = password_checks(username, new_password, new_password2)

        if not ok:
            self._set_changing_password(False)
            self._set_password_change_status(msg, error=True)
            self.ui.leNewPassword.setFocus()
            return

        self._set_changing_password(True)
        self._backend.user_change_password(current_password=current_password,
                                           new_password=new_password)

    @QtCore.Slot()
    def _srp_change_password_ok(self):
        """
        TRIGGERS:
            self._backend.signaler.srp_password_change_ok

        Callback used to display a successfully changed password.
        """
        new_password = self.ui.leNewPassword.text()
        logger.debug("SRP password changed successfully.")

        if self._mx_provided:
            self._backend.soledad_change_password(new_password=new_password)
        else:
            self._change_password_success()

    @QtCore.Slot(unicode)
    def _srp_change_password_problem(self, msg):
        """
        TRIGGERS:
            self._backend.signaler.srp_password_change_error
            self._backend.signaler.srp_password_change_badpw

        Callback used to display an error on changing password.

        :param msg: the message to show to the user.
        :type msg: unicode
        """
        logger.error("Error changing password")
        self._set_password_change_status(msg, error=True)
        self._set_changing_password(False)

    @QtCore.Slot()
    def _soledad_change_password_ok(self):
        """
        TRIGGERS:
            Signaler.soledad_password_change_ok

        Soledad password change went OK.
        """
        logger.debug("Soledad password changed successfully.")
        self._change_password_success()

    def _change_password_success(self):
        """
        Callback used to display a successfully changed password.
        """
        logger.debug("Soledad password changed successfully.")

        self._set_password_change_status(
            self.tr("Password changed successfully."), success=True)
        self._clear_password_inputs()
        self._set_changing_password(False)

    @QtCore.Slot(unicode)
    def _soledad_change_password_problem(self, msg):
        """
        TRIGGERS:
            Signaler.soledad_password_change_error

        Callback used to display an error on changing password.

        :param msg: the message to show to the user.
        :type msg: unicode
        """
        logger.error("Error changing soledad password")
        self._set_password_change_status(msg, error=True)
        self._set_changing_password(False)

    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
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        """
        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()
        for provider in self._settings.get_configured_providers():
            self.ui.cbProvidersServices.addItem(provider)

    def _select_provider_by_name(self, name):
        """
        Given a provider name/domain, selects it in the combobox.

        :param name: name or domain for the provider
        :type name: str
        """
        provider_index = self.ui.cbProvidersServices.findText(name)
        self.ui.cbProvidersServices.setCurrentIndex(provider_index)

    @QtCore.Slot(str, int)
    def _service_selection_changed(self, service, state):
        """
        TRIGGERS:
            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)

    @QtCore.Slot(str)
    def _populate_services(self, domain):
        """
        TRIGGERS:
            self.ui.cbProvidersServices.currentIndexChanged[unicode]

        Fill the services list with the selected provider's services.

        :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)

        if not domain:
            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)

        self._backend.provider_get_supported_services(domain=domain)

    @QtCore.Slot(str)
    def _load_services(self, services):
        """
        TRIGGERS:
            self.ui.cbProvidersServices.currentIndexChanged[unicode]

        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
        """
        domain = self.ui.cbProvidersServices.currentText()
        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)
                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,))

    @QtCore.Slot(str)
    def _save_enabled_services(self, provider):
        """
        TRIGGERS:
            self.ui.pbSaveServices.clicked

        Saves the new enabled services 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)
        self.preferences_saved.emit()

    def _backend_connect(self):
        """
        Helper to connect to backend signals
        """
        sig = self._leap_signaler

        sig.prov_get_supported_services.connect(self._load_services)

        sig.srp_password_change_ok.connect(self._srp_change_password_ok)

        pwd_change_error = lambda: self._srp_change_password_problem(
            self.tr("There was a problem changing the password."))
        sig.srp_password_change_error.connect(pwd_change_error)

        pwd_change_badpw = lambda: self._srp_change_password_problem(
            self.tr("You did not enter a correct current password."))
        sig.srp_password_change_badpw.connect(pwd_change_badpw)

        sig.soledad_password_change_ok.connect(
            self._soledad_change_password_ok)

        sig.soledad_password_change_error.connect(
            self._soledad_change_password_problem)
예제 #8
0
class EIPPreferencesWindow(QtGui.QDialog):
    """
    Window that displays the EIP preferences.
    """
    def __init__(self, parent):
        """
        :param parent: parent object of the EIPPreferencesWindow.
        :parent type: QWidget
        """
        QtGui.QDialog.__init__(self, parent)
        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")

        self._settings = LeapSettings()

        # Load UI
        self.ui = Ui_EIPPreferences()
        self.ui.setupUi(self)
        self.ui.lblProvidersGatewayStatus.setVisible(False)
        self.ui.lblAutoStartEIPStatus.setVisible(False)

        # Connections
        self.ui.cbProvidersGateway.currentIndexChanged[unicode].connect(
            self._populate_gateways)

        self.ui.cbGateways.currentIndexChanged[unicode].connect(
            lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))

        self.ui.cbProvidersEIP.currentIndexChanged[unicode].connect(
            lambda x: self.ui.lblAutoStartEIPStatus.setVisible(False))

        self.ui.cbAutoStartEIP.toggled.connect(
            lambda x: self.ui.lblAutoStartEIPStatus.setVisible(False))

        self.ui.pbSaveAutoStartEIP.clicked.connect(self._save_auto_start_eip)

        self._add_configured_providers()

        # Load auto start EIP settings
        self.ui.cbAutoStartEIP.setChecked(self._settings.get_autostart_eip())
        default_provider = self._settings.get_defaultprovider()
        idx = self.ui.cbProvidersEIP.findText(default_provider)
        self.ui.cbProvidersEIP.setCurrentIndex(idx)

    def _save_auto_start_eip(self):
        """
        SLOT
        TRIGGER:
            self.ui.cbAutoStartEIP.toggled

        Saves the automatic start of EIP user preference.
        """
        default_provider = self.ui.cbProvidersEIP.currentText()
        enabled = self.ui.cbAutoStartEIP.isChecked()

        self._settings.set_autostart_eip(enabled)
        self._settings.set_defaultprovider(default_provider)

        self.ui.lblAutoStartEIPStatus.show()
        logger.debug('Auto start EIP saved: {0} {1}.'.format(
            default_provider, enabled))

    def _set_providers_gateway_status(self, status, success=False,
                                      error=False):
        """
        Sets the status label for the gateway change.

        :param status: status message to display, can be HTML
        :type status: str
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        :param error: is set to True if we should display the
                        message as red
        :type error: bool
        """
        if success:
            status = "<font color='green'><b>%s</b></font>" % (status,)
        elif error:
            status = "<font color='red'><b>%s</b></font>" % (status,)

        self.ui.lblProvidersGatewayStatus.setVisible(True)
        self.ui.lblProvidersGatewayStatus.setText(status)

    def _add_configured_providers(self):
        """
        Add the client's configured providers to the providers combo boxes.
        """
        self.ui.cbProvidersGateway.clear()
        self.ui.cbProvidersEIP.clear()
        providers = self._settings.get_configured_providers()
        if not providers:
            self.ui.gbAutomaticEIP.setEnabled(False)
            self.ui.gbGatewaySelector.setEnabled(False)
            return

        for provider in providers:
            self.ui.cbProvidersGateway.addItem(provider)
            self.ui.cbProvidersEIP.addItem(provider)

    def _save_selected_gateway(self, provider):
        """
        SLOT
        TRIGGERS:
            self.ui.pbSaveGateway.clicked

        Saves the new gateway setting to the configuration file.

        :param provider: the provider config that we need to save.
        :type provider: str
        """
        gateway = self.ui.cbGateways.currentText()

        if gateway == self.AUTOMATIC_GATEWAY_LABEL:
            gateway = self._settings.GATEWAY_AUTOMATIC
        else:
            idx = self.ui.cbGateways.currentIndex()
            gateway = self.ui.cbGateways.itemData(idx)

        self._settings.set_selected_gateway(provider, gateway)

        msg = self.tr(
            "Gateway settings for provider '{0}' saved.").format(provider)
        self._set_providers_gateway_status(msg, success=True)

    def _populate_gateways(self, domain):
        """
        SLOT
        TRIGGERS:
            self.ui.cbProvidersGateway.currentIndexChanged[unicode]

        Loads the gateways that the provider provides into the UI for
        the user to select.

        :param domain: the domain of the provider to load gateways from.
        :type domain: str
        """
        # We hide the maybe-visible status label after a change
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        if not domain:
            return

        try:
            # disconnect previously connected save method
            self.ui.pbSaveGateway.clicked.disconnect()
        except RuntimeError:
            pass  # Signal was not connected

        # set the proper connection for the 'save' button
        save_gateway = partial(self._save_selected_gateway, domain)
        self.ui.pbSaveGateway.clicked.connect(save_gateway)

        eip_config = EIPConfig()
        provider_config = ProviderConfig.get_provider_config(domain)

        eip_config_path = os.path.join("leap", "providers",
                                       domain, "eip-service.json")
        api_version = provider_config.get_api_version()
        eip_config.set_api_version(api_version)
        eip_loaded = eip_config.load(eip_config_path)

        if not eip_loaded or provider_config is None:
            self._set_providers_gateway_status(
                self.tr("There was a problem with configuration files."),
                error=True)
            return

        gateways = VPNGatewaySelector(eip_config).get_gateways_list()
        logger.debug(gateways)

        self.ui.cbGateways.clear()
        self.ui.cbGateways.addItem(self.AUTOMATIC_GATEWAY_LABEL)

        # Add the available gateways and
        # select the one stored in configuration file.
        selected_gateway = self._settings.get_selected_gateway(domain)
        index = 0
        for idx, (gw_name, gw_ip) in enumerate(gateways):
            gateway = "{0} ({1})".format(gw_name, gw_ip)
            self.ui.cbGateways.addItem(gateway, gw_ip)
            if gw_ip == selected_gateway:
                index = idx + 1

        self.ui.cbGateways.setCurrentIndex(index)
class EIPPreferencesWindow(QtGui.QDialog):
    """
    Window that displays the EIP preferences.
    """
    def __init__(self, parent, domain, backend, leap_signaler):
        """
        :param parent: parent object of the EIPPreferencesWindow.
        :type parent: QWidget
        :param domain: the selected by default domain.
        :type domain: unicode
        :param backend: Backend being used
        :type backend: Backend
        """
        QtGui.QDialog.__init__(self, parent)
        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")

        self._settings = LeapSettings()
        self._leap_signaler = leap_signaler
        self._backend = backend

        # Load UI
        self.ui = Ui_EIPPreferences()
        self.ui.setupUi(self)
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        # Connections
        self.ui.cbProvidersGateway.currentIndexChanged[int].connect(
            self._populate_gateways)

        self.ui.cbGateways.currentIndexChanged[unicode].connect(
            lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))

        self._selected_domain = domain
        self._configured_providers = []

        self._backend_connect()
        self._add_configured_providers()

    def _set_providers_gateway_status(self, status, success=False,
                                      error=False):
        """
        Sets the status label for the gateway change.

        :param status: status message to display, can be HTML
        :type status: str
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        :param error: is set to True if we should display the
                        message as red
        :type error: bool
        """
        if success:
            status = "<font color='green'><b>%s</b></font>" % (status,)
        elif error:
            status = "<font color='red'><b>%s</b></font>" % (status,)

        self.ui.lblProvidersGatewayStatus.setVisible(True)
        self.ui.lblProvidersGatewayStatus.setText(status)

    def _add_configured_providers(self):
        """
        Add the client's configured providers to the providers combo boxes.
        """
        providers = self._settings.get_configured_providers()
        if not providers:
            return

        self._backend.eip_get_initialized_providers(domains=providers)

    @QtCore.Slot(list)
    def _load_providers_in_combo(self, providers):
        """
        TRIGGERS:
            Signaler.eip_get_initialized_providers

        Add the client's configured providers to the providers combo boxes.

        :param providers: the list of providers to add and whether each one is
                          initialized or not.
        :type providers: list of tuples (str, bool)
        """
        self.ui.cbProvidersGateway.clear()
        if not providers:
            self.ui.gbGatewaySelector.setEnabled(False)
            return

        # block signals so the currentIndexChanged slot doesn't get triggered
        self.ui.cbProvidersGateway.blockSignals(True)
        for provider, is_initialized in providers:
            label = provider
            if not is_initialized:
                label += self.tr(" (uninitialized)")
            self.ui.cbProvidersGateway.addItem(label, userData=provider)
        self.ui.cbProvidersGateway.blockSignals(False)

        # Select provider by name
        domain = self._selected_domain
        if domain is not None:
            provider_index = self.ui.cbProvidersGateway.findText(
                domain, QtCore.Qt.MatchStartsWith)
            self.ui.cbProvidersGateway.setCurrentIndex(provider_index)

    @QtCore.Slot(str)
    def _save_selected_gateway(self, provider):
        """
        TRIGGERS:
            self.ui.pbSaveGateway.clicked

        Saves the new gateway setting to the configuration file.

        :param provider: the provider config that we need to save.
        :type provider: str
        """
        gateway = self.ui.cbGateways.currentText()

        if gateway == self.AUTOMATIC_GATEWAY_LABEL:
            gateway = self._settings.GATEWAY_AUTOMATIC
        else:
            idx = self.ui.cbGateways.currentIndex()
            gateway = self.ui.cbGateways.itemData(idx)

        self._settings.set_selected_gateway(provider, gateway)
        self._backend.settings_set_selected_gateway(provider=provider,
                                                    gateway=gateway)

        msg = self.tr(
            "Gateway settings for provider '{0}' saved.").format(provider)
        self._set_providers_gateway_status(msg, success=True)

    @QtCore.Slot(int)
    def _populate_gateways(self, domain_idx):
        """
        TRIGGERS:
            self.ui.cbProvidersGateway.currentIndexChanged[unicode]

        Loads the gateways that the provider provides into the UI for
        the user to select.

        :param domain: the domain index of the provider to load gateways from.
        :type domain: int
        """
        # We hide the maybe-visible status label after a change
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        if domain_idx == -1:
            return

        domain = self.ui.cbProvidersGateway.itemData(domain_idx)
        self._selected_domain = domain

        self._backend.eip_get_gateways_list(domain=domain)

    @QtCore.Slot(list)
    def _update_gateways_list(self, gateways):
        """
        TRIGGERS:
            Signaler.eip_get_gateways_list

        :param gateways: a list of gateways
        :type gateways: list of unicode

        Add the available gateways and select the one stored in configuration
        file.
        """
        self.ui.pbSaveGateway.setEnabled(True)
        self.ui.cbGateways.setEnabled(True)

        self.ui.cbGateways.clear()
        self.ui.cbGateways.addItem(self.AUTOMATIC_GATEWAY_LABEL)

        try:
            # disconnect previously connected save method
            self.ui.pbSaveGateway.clicked.disconnect()
        except RuntimeError:
            pass  # Signal was not connected

        # set the proper connection for the 'save' button
        domain = self._selected_domain
        save_gateway = partial(self._save_selected_gateway, domain)
        self.ui.pbSaveGateway.clicked.connect(save_gateway)

        selected_gateway = self._settings.get_selected_gateway(
            self._selected_domain)

        index = 0
        for idx, (gw_name, gw_ip) in enumerate(gateways):
            gateway = "{0} ({1})".format(gw_name, gw_ip)
            self.ui.cbGateways.addItem(gateway, gw_ip)
            if gw_ip == selected_gateway:
                index = idx + 1

        self.ui.cbGateways.setCurrentIndex(index)

    @QtCore.Slot()
    def _gateways_list_error(self):
        """
        TRIGGERS:
            Signaler.eip_get_gateways_list_error

        An error has occurred retrieving the gateway list so we inform the
        user.
        """
        self._set_providers_gateway_status(
            self.tr("There was a problem with configuration files."),
            error=True)
        self.ui.pbSaveGateway.setEnabled(False)
        self.ui.cbGateways.setEnabled(False)

    @QtCore.Slot()
    def _gateways_list_uninitialized(self):
        """
        TRIGGERS:
            Signaler.eip_uninitialized_provider

        The requested provider in not initialized yet, so we give the user an
        error msg.
        """
        self._set_providers_gateway_status(
            self.tr("This is an uninitialized provider, please log in first."),
            error=True)
        self.ui.pbSaveGateway.setEnabled(False)
        self.ui.cbGateways.setEnabled(False)

    def _backend_connect(self):
        sig = self._leap_signaler
        sig.eip_get_gateways_list.connect(self._update_gateways_list)
        sig.eip_get_gateways_list_error.connect(self._gateways_list_error)
        sig.eip_uninitialized_provider.connect(
            self._gateways_list_uninitialized)
        sig.eip_get_initialized_providers.connect(
            self._load_providers_in_combo)
예제 #10
0
    def _load_configured_providers_with_pinned(self, pinned):
        """
        Once we have the pinned providers from the backend, we
        continue setting everything up

        :param pinned: list of pinned providers
        :type pinned: list of str


        How the combobox items are arranged:
        -----------------------------------

        First run:

            demo.bitmask.net
            --
            pinned2.org
            pinned1.org
            pinned3.org

        After some usage:

            added-by-user.org
            pinned-but-then-used.org
            ---
            demo.bitmask.net
            pinned1.org
            pinned3.org
            pinned2.org

        In other words:
            * There are two sections.
            * Section one consists of all the providers that the user has used.
              If this is empty, than use demo.bitmask.net for this section.
              This list is sorted alphabetically.
            * Section two consists of all the pinned or 'pre seeded' providers,
              minus any providers that are now in section one. This last list
              is in random order.
        """
        ls = LeapSettings()
        user_added = ls.get_configured_providers()
        if not user_added and not pinned:
            self.ui.rbExistingProvider.setEnabled(False)
            self.ui.label_8.setEnabled(False)  # 'https://' label
            self.ui.cbProviders.setEnabled(False)
            return

        user_added.sort()

        if not user_added:
            user_added = [pinned.pop(0)]

        # separate unused pinned providers from user added ones
        for p in user_added:
            if p in pinned:
                pinned.remove(p)

        if user_added:
            self.ui.cbProviders.addItems(user_added)

        if user_added and pinned:
            self.ui.cbProviders.addItem('---')

        if pinned:
            random.shuffle(pinned)  # don't prioritize alphabetically
            self.ui.cbProviders.addItems(pinned)

        # We have configured providers, so by default we select the
        # 'Use existing provider' option.
        self.ui.rbExistingProvider.setChecked(True)

        # We need to set it as complete explicitly
        self.page(self.INTRO_PAGE).set_completed()
class PreferencesWindow(QtGui.QDialog):
    """
    Window that displays the preferences.
    """
    def __init__(self, parent, srp_auth, provider_config, soledad, domain):
        """
        :param parent: parent object of the PreferencesWindow.
        :parent type: QWidget
        :param srp_auth: SRPAuth object configured in the main app.
        :type srp_auth: SRPAuth
        :param provider_config: ProviderConfig object.
        :type provider_config: ProviderConfig
        :param soledad: Soledad instance
        :type soledad: Soledad
        :param domain: the selected domain in the login widget
        :type domain: unicode
        """
        QtGui.QDialog.__init__(self, parent)
        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")

        self._srp_auth = srp_auth
        self._settings = LeapSettings()
        self._soledad = soledad

        # Load UI
        self.ui = Ui_Preferences()
        self.ui.setupUi(self)
        self.ui.lblPasswordChangeStatus.setVisible(False)
        self.ui.lblProvidersServicesStatus.setVisible(False)

        self._selected_services = set()

        # Connections
        self.ui.pbChangePassword.clicked.connect(self._change_password)
        self.ui.cbProvidersServices.currentIndexChanged[unicode].connect(
            self._populate_services)

        if not self._settings.get_configured_providers():
            self.ui.gbEnabledServices.setEnabled(False)
        else:
            self._add_configured_providers()

        pw_enabled = False

        # check if the user is logged in
        if srp_auth is not None and srp_auth.get_token() is not None:
            # check if provider has 'mx' ...
            if provider_config.provides_mx():
                enabled_services = self._settings.get_enabled_services(domain)
                mx_name = get_service_display_name(MX_SERVICE)

                # ... and if the user have it enabled
                if MX_SERVICE not in enabled_services:
                    msg = self.tr("You need to enable {0} in order to change "
                                  "the password.".format(mx_name))
                    self._set_password_change_status(msg, error=True)
                else:
                    if sameProxiedObjects(self._soledad, None):
                        msg = self.tr(
                            "You need to wait until {0} is ready in "
                            "order to change the password.".format(mx_name))
                        self._set_password_change_status(msg)
                    else:
                        # Soledad is bootstrapped
                        pw_enabled = True
            else:
                pw_enabled = True
        else:
            msg = self.tr(
                "In order to change your password you need to be logged in.")
            self._set_password_change_status(msg)

        self._select_provider_by_name(domain)

        self.ui.gbPasswordChange.setEnabled(pw_enabled)

    def set_soledad_ready(self):
        """
        SLOT
        TRIGGERS:
            parent.soledad_ready

        It notifies when the soledad object as ready to use.
        """
        self.ui.lblPasswordChangeStatus.setVisible(False)
        self.ui.gbPasswordChange.setEnabled(True)

    def _set_password_change_status(self, status, error=False, success=False):
        """
        Sets the status label for the password change.

        :param status: status message to display, can be HTML
        :type status: str
        """
        if error:
            status = "<font color='red'><b>%s</b></font>" % (status,)
        elif success:
            status = "<font color='green'><b>%s</b></font>" % (status,)

        if not self.ui.gbPasswordChange.isEnabled():
            status = "<font color='black'>%s</font>" % (status,)

        self.ui.lblPasswordChangeStatus.setVisible(True)
        self.ui.lblPasswordChangeStatus.setText(status)

    def _set_changing_password(self, disable):
        """
        Enables or disables the widgets in the password change group box.

        :param disable: True if the widgets should be disabled and
                        it displays the status label that shows that is
                        changing the password.
                        False if they should be enabled.
        :type disable: bool
        """
        if disable:
            self._set_password_change_status(self.tr("Changing password..."))

        self.ui.leCurrentPassword.setEnabled(not disable)
        self.ui.leNewPassword.setEnabled(not disable)
        self.ui.leNewPassword2.setEnabled(not disable)
        self.ui.pbChangePassword.setEnabled(not disable)

    def _change_password(self):
        """
        SLOT
        TRIGGERS:
            self.ui.pbChangePassword.clicked

        Changes the user's password if the inputboxes are correctly filled.
        """
        username = self._srp_auth.get_username()
        current_password = self.ui.leCurrentPassword.text()
        new_password = self.ui.leNewPassword.text()
        new_password2 = self.ui.leNewPassword2.text()

        ok, msg = basic_password_checks(username, new_password, new_password2)

        if not ok:
            self._set_changing_password(False)
            self._set_password_change_status(msg, error=True)
            self.ui.leNewPassword.setFocus()
            return

        self._set_changing_password(True)
        d = self._srp_auth.change_password(current_password, new_password)
        d.addCallback(partial(self._change_password_success, new_password))
        d.addErrback(self._change_password_problem)

    def _change_password_success(self, new_password, _):
        """
        Callback used to display a successfully performed action.

        :param new_password: the new password for the user.
        :type new_password: str.
        :param _: the returned data from self._srp_auth.change_password
                  Ignored
        """
        logger.debug("SRP password changed successfully.")
        try:
            self._soledad.change_passphrase(new_password)
            logger.debug("Soledad password changed successfully.")
        except NoStorageSecret:
            logger.debug(
                "No storage secret for password change in Soledad.")

        self._set_password_change_status(
            self.tr("Password changed successfully."), success=True)
        self._clear_password_inputs()
        self._set_changing_password(False)

    def _change_password_problem(self, failure):
        """
        Errback called if there was a problem with the deferred.
        Also is used to display an error message.

        :param failure: the cause of the method failed.
        :type failure: twisted.python.Failure
        """
        logger.error("Error changing password: %s", (failure, ))
        problem = self.tr("There was a problem changing the password.")

        if failure.check(SRPAuthBadUserOrPassword):
            problem = self.tr("You did not enter a correct current password.")

        self._set_password_change_status(problem, error=True)

        self._set_changing_password(False)
        failure.trap(Exception)

    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
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        """
        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()
        for provider in self._settings.get_configured_providers():
            self.ui.cbProvidersServices.addItem(provider)

    def _select_provider_by_name(self, name):
        """
        Given a provider name/domain, selects it in the combobox.

        :param name: name or domain for the provider
        :type name: str
        """
        provider_index = self.ui.cbProvidersServices.findText(name)
        self.ui.cbProvidersServices.setCurrentIndex(provider_index)

    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.currentIndexChanged[unicode]

        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)

        if not domain:
            return

        provider_config = self._get_provider_config(domain)
        if provider_config is None:
            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(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)
                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):
        """
        SLOT
        TRIGGERS:
            self.ui.pbSaveServices.clicked

        Saves the new enabled services 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)

    def _get_provider_config(self, domain):
        """
        Helper to return a valid Provider Config from the domain name.

        :param domain: the domain name of the provider.
        :type domain: str

        :rtype: ProviderConfig or None if there is a problem loading the config
        """
        provider_config = ProviderConfig()
        provider_config_path = os.path.join(
            "leap", "providers", domain, "provider.json")

        if not provider_config.load(provider_config_path):
            provider_config = None

        return provider_config
class EIPPreferencesWindow(QtGui.QDialog):
    """
    Window that displays the EIP preferences.
    """
    def __init__(self, parent, domain):
        """
        :param parent: parent object of the EIPPreferencesWindow.
        :type parent: QWidget
        :param domain: the selected by default domain.
        :type domain: unicode
        """
        QtGui.QDialog.__init__(self, parent)
        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")

        self._settings = LeapSettings()

        # Load UI
        self.ui = Ui_EIPPreferences()
        self.ui.setupUi(self)
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        # Connections
        self.ui.cbProvidersGateway.currentIndexChanged[int].connect(
            self._populate_gateways)

        self.ui.cbGateways.currentIndexChanged[unicode].connect(
            lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))

        self._add_configured_providers(domain)

    def _set_providers_gateway_status(self, status, success=False,
                                      error=False):
        """
        Sets the status label for the gateway change.

        :param status: status message to display, can be HTML
        :type status: str
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        :param error: is set to True if we should display the
                        message as red
        :type error: bool
        """
        if success:
            status = "<font color='green'><b>%s</b></font>" % (status,)
        elif error:
            status = "<font color='red'><b>%s</b></font>" % (status,)

        self.ui.lblProvidersGatewayStatus.setVisible(True)
        self.ui.lblProvidersGatewayStatus.setText(status)

    def _add_configured_providers(self, domain=None):
        """
        Add the client's configured providers to the providers combo boxes.

        :param domain: the domain to be selected by default.
        :type domain: unicode
        """
        self.ui.cbProvidersGateway.clear()
        providers = self._settings.get_configured_providers()
        if not providers:
            self.ui.gbGatewaySelector.setEnabled(False)
            return

        for provider in providers:
            label = provider
            eip_config_path = get_eipconfig_path(provider, relative=False)
            if not os.path.isfile(eip_config_path):
                label = provider + self.tr(" (uninitialized)")
            self.ui.cbProvidersGateway.addItem(label, userData=provider)

        # Select provider by name
        if domain is not None:
            provider_index = self.ui.cbProvidersGateway.findText(
                domain, QtCore.Qt.MatchStartsWith)
            self.ui.cbProvidersGateway.setCurrentIndex(provider_index)

    def _save_selected_gateway(self, provider):
        """
        SLOT
        TRIGGERS:
            self.ui.pbSaveGateway.clicked

        Saves the new gateway setting to the configuration file.

        :param provider: the provider config that we need to save.
        :type provider: str
        """
        gateway = self.ui.cbGateways.currentText()

        if gateway == self.AUTOMATIC_GATEWAY_LABEL:
            gateway = self._settings.GATEWAY_AUTOMATIC
        else:
            idx = self.ui.cbGateways.currentIndex()
            gateway = self.ui.cbGateways.itemData(idx)

        self._settings.set_selected_gateway(provider, gateway)

        msg = self.tr(
            "Gateway settings for provider '{0}' saved.").format(provider)
        self._set_providers_gateway_status(msg, success=True)

    def _populate_gateways(self, domain_idx):
        """
        SLOT
        TRIGGERS:
            self.ui.cbProvidersGateway.currentIndexChanged[unicode]

        Loads the gateways that the provider provides into the UI for
        the user to select.

        :param domain: the domain index of the provider to load gateways from.
        :type domain: int
        """
        # We hide the maybe-visible status label after a change
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        if domain_idx == -1:
            return

        domain = self.ui.cbProvidersGateway.itemData(domain_idx)

        if not os.path.isfile(get_eipconfig_path(domain, relative=False)):
            self._set_providers_gateway_status(
                self.tr("This is an uninitialized provider, "
                        "please log in first."),
                error=True)
            self.ui.pbSaveGateway.setEnabled(False)
            self.ui.cbGateways.setEnabled(False)
            return
        else:
            self.ui.pbSaveGateway.setEnabled(True)
            self.ui.cbGateways.setEnabled(True)

        try:
            # disconnect previously connected save method
            self.ui.pbSaveGateway.clicked.disconnect()
        except RuntimeError:
            pass  # Signal was not connected

        # set the proper connection for the 'save' button
        save_gateway = partial(self._save_selected_gateway, domain)
        self.ui.pbSaveGateway.clicked.connect(save_gateway)

        eip_config = EIPConfig()
        provider_config = ProviderConfig.get_provider_config(domain)

        api_version = provider_config.get_api_version()
        eip_config.set_api_version(api_version)
        eip_loaded = eip_config.load(get_eipconfig_path(domain))

        if not eip_loaded or provider_config is None:
            self._set_providers_gateway_status(
                self.tr("There was a problem with configuration files."),
                error=True)
            return

        gateways = VPNGatewaySelector(eip_config).get_gateways_list()
        logger.debug(gateways)

        self.ui.cbGateways.clear()
        self.ui.cbGateways.addItem(self.AUTOMATIC_GATEWAY_LABEL)

        # Add the available gateways and
        # select the one stored in configuration file.
        selected_gateway = self._settings.get_selected_gateway(domain)
        index = 0
        for idx, (gw_name, gw_ip) in enumerate(gateways):
            gateway = "{0} ({1})".format(gw_name, gw_ip)
            self.ui.cbGateways.addItem(gateway, gw_ip)
            if gw_ip == selected_gateway:
                index = idx + 1

        self.ui.cbGateways.setCurrentIndex(index)
예제 #13
0
class EIPPreferencesWindow(QtGui.QDialog):
    """
    Window that displays the EIP preferences.
    """
    def __init__(self, parent, domain, backend, leap_signaler):
        """
        :param parent: parent object of the EIPPreferencesWindow.
        :type parent: QWidget
        :param domain: the selected by default domain.
        :type domain: unicode
        :param backend: Backend being used
        :type backend: Backend
        """
        QtGui.QDialog.__init__(self, parent)
        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")

        self._settings = LeapSettings()
        self._leap_signaler = leap_signaler
        self._backend = backend

        # Load UI
        self.ui = Ui_EIPPreferences()
        self.ui.setupUi(self)
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        # Connections
        self.ui.cbProvidersGateway.currentIndexChanged[int].connect(
            self._populate_gateways)

        self.ui.cbGateways.currentIndexChanged[unicode].connect(
            lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))

        self._selected_domain = domain
        self._configured_providers = []

        self._backend_connect()
        self._add_configured_providers()

    def _set_providers_gateway_status(self,
                                      status,
                                      success=False,
                                      error=False):
        """
        Sets the status label for the gateway change.

        :param status: status message to display, can be HTML
        :type status: str
        :param success: is set to True if we should display the
                        message as green
        :type success: bool
        :param error: is set to True if we should display the
                        message as red
        :type error: bool
        """
        if success:
            status = "<font color='green'><b>%s</b></font>" % (status, )
        elif error:
            status = "<font color='red'><b>%s</b></font>" % (status, )

        self.ui.lblProvidersGatewayStatus.setVisible(True)
        self.ui.lblProvidersGatewayStatus.setText(status)

    def _add_configured_providers(self):
        """
        Add the client's configured providers to the providers combo boxes.
        """
        providers = self._settings.get_configured_providers()
        if not providers:
            return

        self._backend.eip_get_initialized_providers(domains=providers)

    def _load_providers_in_combo(self, providers):
        """
        TRIGGERS:
            Signaler.eip_get_initialized_providers

        Add the client's configured providers to the providers combo boxes.

        :param providers: the list of providers to add and whether each one is
                          initialized or not.
        :type providers: list of tuples (str, bool)
        """
        self.ui.cbProvidersGateway.clear()
        if not providers:
            self.ui.gbGatewaySelector.setEnabled(False)
            return

        # block signals so the currentIndexChanged slot doesn't get triggered
        self.ui.cbProvidersGateway.blockSignals(True)
        for provider, is_initialized in providers:
            label = provider
            if not is_initialized:
                label += self.tr(" (uninitialized)")
            self.ui.cbProvidersGateway.addItem(label, userData=provider)
        self.ui.cbProvidersGateway.blockSignals(False)

        # Select provider by name
        domain = self._selected_domain
        if domain is not None:
            provider_index = self.ui.cbProvidersGateway.findText(
                domain, QtCore.Qt.MatchStartsWith)
            self.ui.cbProvidersGateway.setCurrentIndex(provider_index)

    def _save_selected_gateway(self, provider):
        """
        TRIGGERS:
            self.ui.pbSaveGateway.clicked

        Saves the new gateway setting to the configuration file.

        :param provider: the provider config that we need to save.
        :type provider: str
        """
        gateway = self.ui.cbGateways.currentText()

        if gateway == self.AUTOMATIC_GATEWAY_LABEL:
            gateway = self._settings.GATEWAY_AUTOMATIC
        else:
            idx = self.ui.cbGateways.currentIndex()
            gateway = self.ui.cbGateways.itemData(idx)

        self._settings.set_selected_gateway(provider, gateway)
        self._backend.settings_set_selected_gateway(provider=provider,
                                                    gateway=gateway)

        msg = self.tr("Gateway settings for provider '{0}' saved.").format(
            provider)
        self._set_providers_gateway_status(msg, success=True)

    def _populate_gateways(self, domain_idx):
        """
        TRIGGERS:
            self.ui.cbProvidersGateway.currentIndexChanged[unicode]

        Loads the gateways that the provider provides into the UI for
        the user to select.

        :param domain: the domain index of the provider to load gateways from.
        :type domain: int
        """
        # We hide the maybe-visible status label after a change
        self.ui.lblProvidersGatewayStatus.setVisible(False)

        if domain_idx == -1:
            return

        domain = self.ui.cbProvidersGateway.itemData(domain_idx)
        self._selected_domain = domain

        self._backend.eip_get_gateways_list(domain=domain)

    def _update_gateways_list(self, gateways):
        """
        TRIGGERS:
            Signaler.eip_get_gateways_list

        :param gateways: a list of gateways
        :type gateways: list of unicode

        Add the available gateways and select the one stored in configuration
        file.
        """
        self.ui.pbSaveGateway.setEnabled(True)
        self.ui.cbGateways.setEnabled(True)

        self.ui.cbGateways.clear()
        self.ui.cbGateways.addItem(self.AUTOMATIC_GATEWAY_LABEL)

        try:
            # disconnect previously connected save method
            self.ui.pbSaveGateway.clicked.disconnect()
        except RuntimeError:
            pass  # Signal was not connected

        # set the proper connection for the 'save' button
        domain = self._selected_domain
        save_gateway = partial(self._save_selected_gateway, domain)
        self.ui.pbSaveGateway.clicked.connect(save_gateway)

        selected_gateway = self._settings.get_selected_gateway(
            self._selected_domain)

        index = 0
        for idx, (gw_name, gw_ip) in enumerate(gateways):
            gateway = "{0} ({1})".format(gw_name, gw_ip)
            self.ui.cbGateways.addItem(gateway, gw_ip)
            if gw_ip == selected_gateway:
                index = idx + 1

        self.ui.cbGateways.setCurrentIndex(index)

    def _gateways_list_error(self):
        """
        TRIGGERS:
            Signaler.eip_get_gateways_list_error

        An error has occurred retrieving the gateway list so we inform the
        user.
        """
        self._set_providers_gateway_status(
            self.tr("There was a problem with configuration files."),
            error=True)
        self.ui.pbSaveGateway.setEnabled(False)
        self.ui.cbGateways.setEnabled(False)

    def _gateways_list_uninitialized(self):
        """
        TRIGGERS:
            Signaler.eip_uninitialized_provider

        The requested provider in not initialized yet, so we give the user an
        error msg.
        """
        self._set_providers_gateway_status(
            self.tr("This is an uninitialized provider, please log in first."),
            error=True)
        self.ui.pbSaveGateway.setEnabled(False)
        self.ui.cbGateways.setEnabled(False)

    def _backend_connect(self):
        sig = self._leap_signaler
        sig.eip_get_gateways_list.connect(self._update_gateways_list)
        sig.eip_get_gateways_list_error.connect(self._gateways_list_error)
        sig.eip_uninitialized_provider.connect(
            self._gateways_list_uninitialized)
        sig.eip_get_initialized_providers.connect(
            self._load_providers_in_combo)