diff options
| author | Tomás Touceda <chiiph@leap.se> | 2013-08-30 17:02:11 -0300 | 
|---|---|---|
| committer | Tomás Touceda <chiiph@leap.se> | 2013-08-30 17:02:11 -0300 | 
| commit | ce72a0596d0e2f49037747c0c19988874e0801c9 (patch) | |
| tree | 706965282ef392cad5e824bfb77b87154be4ef2d /src | |
| parent | 56275cfa83a69925fdb13fba84ce05be0de6ba57 (diff) | |
| parent | 294e70f80ac8feb7ecac14691051037bf0c84afa (diff) | |
Merge remote-tracking branch 'ivan/feature/3505_preferences-select-gateway-manually' into develop
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/bitmask/config/leapsettings.py | 36 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 156 | ||||
| -rw-r--r-- | src/leap/bitmask/gui/ui/preferences.ui | 199 | ||||
| -rw-r--r-- | src/leap/bitmask/services/eip/eipconfig.py | 41 | ||||
| -rw-r--r-- | src/leap/bitmask/services/eip/vpnlaunchers.py | 12 | 
5 files changed, 335 insertions, 109 deletions
diff --git a/src/leap/bitmask/config/leapsettings.py b/src/leap/bitmask/config/leapsettings.py index ca94129e..ad67b29e 100644 --- a/src/leap/bitmask/config/leapsettings.py +++ b/src/leap/bitmask/config/leapsettings.py @@ -66,6 +66,10 @@ class LeapSettings(object):      REMEMBER_KEY = "RememberUserAndPass"      DEFAULTPROVIDER_KEY = "DefaultProvider"      ALERTMISSING_KEY = "AlertMissingScripts" +    GATEWAY_KEY = "Gateway" + +    # values +    GATEWAY_AUTOMATIC = "Automatic"      def __init__(self, standalone=False):          """ @@ -137,6 +141,38 @@ class LeapSettings(object):          return providers +    def get_selected_gateway(self, provider): +        """ +        Returns the configured gateway for the given provider. + +        :param provider: provider domain +        :type provider: str + +        :rtype: str +        """ +        leap_assert(len(provider) > 0, "We need a nonempty provider") +        gateway_key = "{0}/{1}".format(provider, self.GATEWAY_KEY) +        gateway = self._settings.value(gateway_key, self.GATEWAY_AUTOMATIC) + +        return gateway + +    def set_selected_gateway(self, provider, gateway): +        """ +        Saves the configured gateway for the given provider + +        :param provider: provider domain +        :type provider: str + +        :param gateway: gateway to use as default +        :type gateway: str +        """ + +        leap_assert(len(provider) > 0, "We need a nonempty provider") +        leap_assert_type(gateway, (str, unicode)) + +        gateway_key = "{0}/{1}".format(provider, self.GATEWAY_KEY) +        self._settings.setValue(gateway_key, gateway) +      def get_enabled_services(self, provider):          """          Returns a list of enabled services for the given provider diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 17e12304..c3c3490b 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -30,6 +30,7 @@ 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.eip.eipconfig import EIPConfig, VPNGatewaySelector  from leap.bitmask.services import get_service_display_name  logger = logging.getLogger(__name__) @@ -54,6 +55,7 @@ class PreferencesWindow(QtGui.QDialog):          :type standalone: bool          """          QtGui.QDialog.__init__(self, parent) +        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")          self._srp_auth = srp_auth          self._settings = leap_settings @@ -65,14 +67,19 @@ class PreferencesWindow(QtGui.QDialog):          self.ui.setupUi(self)          self.ui.lblPasswordChangeStatus.setVisible(False)          self.ui.lblProvidersServicesStatus.setVisible(False) +        self.ui.lblProvidersGatewayStatus.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) +        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) @@ -207,6 +214,9 @@ class PreferencesWindow(QtGui.QDialog):          :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,) @@ -214,6 +224,28 @@ class PreferencesWindow(QtGui.QDialog):          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. @@ -250,7 +282,7 @@ class PreferencesWindow(QtGui.QDialog):          """          SLOT          TRIGGERS: -            self.ui.cbProvidersServices.clicked +            self.ui.cbProvidersServices.currentIndexChanged[unicode]          Loads the services that the provider provides into the UI for          the user to enable or disable. @@ -261,10 +293,11 @@ class PreferencesWindow(QtGui.QDialog):          # 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: +            return -        if not domain or not self._provider_config.load(provider_config_path): +        provider_config = self._get_provider_config(domain) +        if provider_config is None:              return          # set the proper connection for the 'save' button @@ -276,7 +309,7 @@ class PreferencesWindow(QtGui.QDialog):          save_services = partial(self._save_enabled_services, domain)          self.ui.pbSaveServices.clicked.connect(save_services) -        services = get_supported(self._provider_config.get_services()) +        services = get_supported(provider_config.get_services())          services_conf = self._settings.get_enabled_services(domain)          # discard changes if other provider is selected @@ -307,7 +340,11 @@ class PreferencesWindow(QtGui.QDialog):      def _save_enabled_services(self, provider):          """ -        Saves the new settings to the configuration file. +        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 @@ -319,3 +356,108 @@ class PreferencesWindow(QtGui.QDialog):              "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) diff --git a/src/leap/bitmask/gui/ui/preferences.ui b/src/leap/bitmask/gui/ui/preferences.ui index b59990b1..e66a2d68 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>453</width> -    <height>463</height> +    <width>503</width> +    <height>529</height>     </rect>    </property>    <property name="windowTitle"> @@ -18,80 +18,77 @@      <normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>    </property>    <layout class="QGridLayout" name="gridLayout_3"> -   <item row="0" column="0"> -    <widget class="QGroupBox" name="gbPasswordChange"> +   <item row="5" column="0"> +    <spacer name="verticalSpacer"> +     <property name="orientation"> +      <enum>Qt::Vertical</enum> +     </property> +     <property name="sizeHint" stdset="0"> +      <size> +       <width>20</width> +       <height>40</height> +      </size> +     </property> +    </spacer> +   </item> +   <item row="1" column="0"> +    <widget class="QGroupBox" name="gbGatewaySelector">       <property name="enabled"> -      <bool>false</bool> +      <bool>true</bool>       </property>       <property name="title"> -      <string>Password Change</string> +      <string>Select gateway for provider</string>       </property> -     <layout class="QFormLayout" name="formLayout"> -      <property name="fieldGrowthPolicy"> -       <enum>QFormLayout::ExpandingFieldsGrow</enum> -      </property> -      <item row="0" column="0"> -       <widget class="QLabel" name="lblCurrentPassword"> -        <property name="text"> -         <string>&Current password:</string> -        </property> -        <property name="buddy"> -         <cstring>leCurrentPassword</cstring> -        </property> -       </widget> -      </item> -      <item row="0" column="1"> -       <widget class="QLineEdit" name="leCurrentPassword"> -        <property name="echoMode"> -         <enum>QLineEdit::Password</enum> -        </property> +     <property name="checkable"> +      <bool>false</bool> +     </property> +     <layout class="QGridLayout" name="gridLayout"> +      <item row="0" column="1" colspan="2"> +       <widget class="QComboBox" name="cbProvidersGateway"> +        <item> +         <property name="text"> +          <string><Select provider></string> +         </property> +        </item>         </widget>        </item> -      <item row="1" column="0"> -       <widget class="QLabel" name="lblNewPassword"> +      <item row="0" column="0"> +       <widget class="QLabel" name="lblSelectProvider">          <property name="text"> -         <string>&New password:</string> +         <string>&Select provider:</string>          </property>          <property name="buddy"> -         <cstring>leNewPassword</cstring> -        </property> -       </widget> -      </item> -      <item row="1" column="1"> -       <widget class="QLineEdit" name="leNewPassword"> -        <property name="echoMode"> -         <enum>QLineEdit::Password</enum> +         <cstring>cbProvidersGateway</cstring>          </property>         </widget>        </item> -      <item row="2" column="0"> -       <widget class="QLabel" name="lblNewPassword2"> +      <item row="1" column="0"> +       <widget class="QLabel" name="label">          <property name="text"> -         <string>&Re-enter new password:</string> -        </property> -        <property name="buddy"> -         <cstring>leNewPassword2</cstring> +         <string>Select gateway:</string>          </property>         </widget>        </item> -      <item row="2" column="1"> -       <widget class="QLineEdit" name="leNewPassword2"> -        <property name="echoMode"> -         <enum>QLineEdit::Password</enum> -        </property> +      <item row="1" column="1" colspan="2"> +       <widget class="QComboBox" name="cbGateways"> +        <item> +         <property name="text"> +          <string>Automatic</string> +         </property> +        </item>         </widget>        </item> -      <item row="4" column="1"> -       <widget class="QPushButton" name="pbChangePassword"> +      <item row="5" column="2"> +       <widget class="QPushButton" name="pbSaveGateway">          <property name="text"> -         <string>Change</string> +         <string>Save this provider settings</string>          </property>         </widget>        </item> -      <item row="3" column="0" colspan="2"> -       <widget class="QLabel" name="lblPasswordChangeStatus"> +      <item row="2" column="0" colspan="3"> +       <widget class="QLabel" name="lblProvidersGatewayStatus">          <property name="text"> -         <string><Password change status></string> +         <string>< Providers Gateway Status ></string>          </property>          <property name="alignment">           <set>Qt::AlignCenter</set> @@ -101,7 +98,7 @@       </layout>      </widget>     </item> -   <item row="2" column="0"> +   <item row="4" column="0">      <widget class="QGroupBox" name="gbEnabledServices">       <property name="title">        <string>Enabled services</string> @@ -158,69 +155,89 @@       </layout>      </widget>     </item> -   <item row="1" column="0"> -    <widget class="QGroupBox" name="gbGatewaySelector"> +   <item row="0" column="0"> +    <widget class="QGroupBox" name="gbPasswordChange">       <property name="enabled">        <bool>false</bool>       </property>       <property name="title"> -      <string>Select gateway for provider</string> -     </property> -     <property name="checkable"> -      <bool>false</bool> +      <string>Password Change</string>       </property> -     <layout class="QGridLayout" name="gridLayout"> +     <layout class="QFormLayout" name="formLayout"> +      <property name="fieldGrowthPolicy"> +       <enum>QFormLayout::ExpandingFieldsGrow</enum> +      </property>        <item row="0" column="0"> -       <widget class="QLabel" name="lblSelectProvider"> +       <widget class="QLabel" name="lblCurrentPassword">          <property name="text"> -         <string>&Select provider:</string> +         <string>&Current password:</string>          </property>          <property name="buddy"> -         <cstring>cbProvidersGateway</cstring> +         <cstring>leCurrentPassword</cstring>          </property>         </widget>        </item>        <item row="0" column="1"> -       <widget class="QComboBox" name="cbProvidersGateway"> -        <item> -         <property name="text"> -          <string><Select provider></string> -         </property> -        </item> +       <widget class="QLineEdit" name="leCurrentPassword"> +        <property name="echoMode"> +         <enum>QLineEdit::Password</enum> +        </property>         </widget>        </item>        <item row="1" column="0"> -       <widget class="QLabel" name="label"> +       <widget class="QLabel" name="lblNewPassword">          <property name="text"> -         <string>Select gateway:</string> +         <string>&New password:</string> +        </property> +        <property name="buddy"> +         <cstring>leNewPassword</cstring>          </property>         </widget>        </item>        <item row="1" column="1"> -       <widget class="QComboBox" name="cbGateways"> -        <item> -         <property name="text"> -          <string>Automatic</string> -         </property> -        </item> +       <widget class="QLineEdit" name="leNewPassword"> +        <property name="echoMode"> +         <enum>QLineEdit::Password</enum> +        </property> +       </widget> +      </item> +      <item row="2" column="0"> +       <widget class="QLabel" name="lblNewPassword2"> +        <property name="text"> +         <string>&Re-enter new password:</string> +        </property> +        <property name="buddy"> +         <cstring>leNewPassword2</cstring> +        </property> +       </widget> +      </item> +      <item row="2" column="1"> +       <widget class="QLineEdit" name="leNewPassword2"> +        <property name="echoMode"> +         <enum>QLineEdit::Password</enum> +        </property> +       </widget> +      </item> +      <item row="4" column="1"> +       <widget class="QPushButton" name="pbChangePassword"> +        <property name="text"> +         <string>Change</string> +        </property> +       </widget> +      </item> +      <item row="3" column="0" colspan="2"> +       <widget class="QLabel" name="lblPasswordChangeStatus"> +        <property name="text"> +         <string><Password change status></string> +        </property> +        <property name="alignment"> +         <set>Qt::AlignCenter</set> +        </property>         </widget>        </item>       </layout>      </widget>     </item> -   <item row="3" column="0"> -    <spacer name="verticalSpacer"> -     <property name="orientation"> -      <enum>Qt::Vertical</enum> -     </property> -     <property name="sizeHint" stdset="0"> -      <size> -       <width>20</width> -       <height>40</height> -      </size> -     </property> -    </spacer> -   </item>    </layout>   </widget>   <resources> diff --git a/src/leap/bitmask/services/eip/eipconfig.py b/src/leap/bitmask/services/eip/eipconfig.py index 843e7397..1f49f9cd 100644 --- a/src/leap/bitmask/services/eip/eipconfig.py +++ b/src/leap/bitmask/services/eip/eipconfig.py @@ -62,11 +62,12 @@ class VPNGatewaySelector(object):          self._eipconfig = eipconfig -    def get_gateways(self): +    def get_gateways_list(self):          """ -        Returns the 4 best gateways, sorted by timezone proximity. +        Returns the existing gateways, sorted by timezone proximity. -        :rtype: list of IPv4Address or IPv6Address object. +        :rtype: list of tuples (location, ip) +                (str, IPv4Address or IPv6Address object)          """          gateways_timezones = []          locations = self._eipconfig.get_locations() @@ -77,19 +78,35 @@ class VPNGatewaySelector(object):              gateway_distance = 99  # if hasn't location -> should go last              if gateway_location is not None: -                gw_offset = int(locations[gateway['location']]['timezone']) +                timezone = locations[gateway['location']]['timezone'] +                gateway_name = locations[gateway['location']].get('name', None) +                if gateway_name is not None: +                    gateway_location = gateway_name + +                gw_offset = int(timezone)                  if gw_offset in self.equivalent_timezones:                      gw_offset = self.equivalent_timezones[gw_offset]                  gateway_distance = self._get_timezone_distance(gw_offset)              ip = self._eipconfig.get_gateway_ip(idx) -            gateways_timezones.append((ip, gateway_distance)) +            gateways_timezones.append((ip, gateway_distance, gateway_location)) -        gateways_timezones = sorted(gateways_timezones, -                                    key=lambda gw: gw[1])[:4] +        gateways_timezones = sorted(gateways_timezones, key=lambda gw: gw[1]) + +        gateways = [] +        for ip, distance, location in gateways_timezones: +            gateways.append((location, ip)) + +        return gateways -        gateways = [ip for ip, dist in gateways_timezones] +    def get_gateways(self): +        """ +        Returns the 4 best gateways, sorted by timezone proximity. + +        :rtype: list of IPv4Address or IPv6Address object. +        """ +        gateways = [ip for location, ip in self.get_gateways_list()][:4]          return gateways      def _get_timezone_distance(self, offset): @@ -246,7 +263,8 @@ if __name__ == "__main__":      console.setFormatter(formatter)      logger.addHandler(console) -    eipconfig = EIPConfig('1') +    eipconfig = EIPConfig() +    eipconfig.set_api_version('1')      try:          eipconfig.get_clusters() @@ -255,9 +273,14 @@ if __name__ == "__main__":          print "Safe value getting is working"      if eipconfig.load("leap/providers/bitmask.net/eip-service.json"): +        print "EIPConfig methods"          print eipconfig.get_clusters()          print eipconfig.get_gateways()          print eipconfig.get_locations()          print eipconfig.get_openvpn_configuration()          print eipconfig.get_serial()          print eipconfig.get_version() +        print "VPNGatewaySelector methods" +        gws = VPNGatewaySelector(eipconfig) +        print gws.get_gateways() +        print gws.get_gateways_list() diff --git a/src/leap/bitmask/services/eip/vpnlaunchers.py b/src/leap/bitmask/services/eip/vpnlaunchers.py index f8c51ad8..15221c48 100644 --- a/src/leap/bitmask/services/eip/vpnlaunchers.py +++ b/src/leap/bitmask/services/eip/vpnlaunchers.py @@ -33,6 +33,7 @@ except ImportError:  from abc import ABCMeta, abstractmethod  from functools import partial +from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.config.providerconfig import ProviderConfig  from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector  from leap.bitmask.util import first @@ -414,8 +415,15 @@ class LinuxVPNLauncher(VPNLauncher):          if openvpn_verb is not None:              args += ['--verb', '%d' % (openvpn_verb,)] -        gateway_selector = VPNGatewaySelector(eipconfig) -        gateways = gateway_selector.get_gateways() +        gateways = [] +        leap_settings = LeapSettings(ProviderConfig.standalone) +        gateway_conf = leap_settings.get_selected_gateway() + +        if gateway_conf == leap_settings.GATEWAY_AUTOMATIC: +            gateway_selector = VPNGatewaySelector(eipconfig) +            gateways = gateway_selector.get_gateways() +        else: +            gateways = [gateway_conf]          if not gateways:              logger.error('No gateway was found!')  | 
