def get_gateways(kls, eipconfig, providerconfig): """ Return the selected gateways for a given provider, looking at the EIP config file. :param eipconfig: eip configuration object :type eipconfig: EIPConfig :param providerconfig: provider specific configuration :type providerconfig: ProviderConfig :rtype: list """ gateways = [] leap_settings = LeapSettings() domain = providerconfig.get_domain() gateway_conf = leap_settings.get_selected_gateway(domain) gateway_selector = VPNGatewaySelector(eipconfig) if gateway_conf == leap_settings.GATEWAY_AUTOMATIC: gateways = gateway_selector.get_gateways() else: gateways = [gateway_conf] if not gateways: logger.error('No gateway was found!') raise VPNLauncherException('No gateway was found!') # this only works for selecting the first gateway, as we're # currently doing. ccodes = gateway_selector.get_gateways_country_code() gateway_ccode = ccodes[gateways[0]] flags.CURRENT_VPN_COUNTRY = gateway_ccode logger.debug("Using gateways ips: {0}".format(', '.join(gateways))) return gateways
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)
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)
def get_vpn_command( self, eipconfig=None, providerconfig=None, socket_host=None, socket_port="9876", openvpn_verb=1 ): """ Returns the platform dependant vpn launching command. It will look for openvpn in the regular paths and algo in path_prefix/apps/eip/ (in case standalone is set) Might raise VPNException. :param eipconfig: eip configuration object :type eipconfig: EIPConfig :param providerconfig: provider specific configuration :type providerconfig: ProviderConfig :param socket_host: either socket path (unix) or socket IP :type socket_host: str :param socket_port: either string "unix" if it's a unix socket, or port otherwise :type socket_port: str :param openvpn_verb: the openvpn verbosity wanted :type openvpn_verb: int :return: A VPN command ready to be launched :rtype: list """ leap_assert(eipconfig, "We need an eip config") leap_assert_type(eipconfig, EIPConfig) leap_assert(providerconfig, "We need a provider config") leap_assert_type(providerconfig, ProviderConfig) leap_assert(socket_host, "We need a socket host!") leap_assert(socket_port, "We need a socket port!") leap_assert(socket_port != "unix", "We cannot use unix sockets in windows!") openvpn_possibilities = which( self.OPENVPN_BIN, path_extension=os.path.join(providerconfig.get_path_prefix(), "..", "apps", "eip") ) if len(openvpn_possibilities) == 0: raise OpenVPNNotFoundException() openvpn = first(openvpn_possibilities) args = [] args += ["--setenv", "LEAPOPENVPN", "1"] if openvpn_verb is not None: args += ["--verb", "%d" % (openvpn_verb,)] gateways = [] leap_settings = LeapSettings(ProviderConfig.standalone) domain = providerconfig.get_domain() gateway_conf = leap_settings.get_selected_gateway(domain) 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!") raise VPNLauncherException(self.tr("No gateway was found!")) logger.debug("Using gateways ips: {0}".format(", ".join(gateways))) for gw in gateways: args += ["--remote", gw, "1194", "udp"] args += [ "--client", "--dev", "tun", ############################################################## # persist-tun makes ping-restart fail because it leaves a # broken routing table ############################################################## # '--persist-tun', "--persist-key", "--tls-client", # We make it log to a file because we cannot attach to the # openvpn process' stdout since it's a process with more # privileges than we are "--log-append", "eip.log", "--remote-cert-tls", "server", ] openvpn_configuration = eipconfig.get_openvpn_configuration() for key, value in openvpn_configuration.items(): args += ["--%s" % (key,), value] ############################################################## # The down-root plugin fails in some situations, so we don't # drop privs for the time being ############################################################## # args += [ # '--user', getpass.getuser(), # #'--group', grp.getgrgid(os.getgroups()[-1]).gr_name # ] args += ["--management-signal", "--management", socket_host, socket_port, "--script-security", "2"] args += [ "--cert", eipconfig.get_client_cert_path(providerconfig), "--key", eipconfig.get_client_cert_path(providerconfig), "--ca", providerconfig.get_ca_cert_path(), ] logger.debug("Running VPN with command:") logger.debug("%s %s" % (openvpn, " ".join(args))) return [openvpn] + args
def get_vpn_command( self, eipconfig=None, providerconfig=None, socket_host=None, socket_port="unix", openvpn_verb=1 ): """ Returns the platform dependant vpn launching command Might raise VPNException. :param eipconfig: eip configuration object :type eipconfig: EIPConfig :param providerconfig: provider specific configuration :type providerconfig: ProviderConfig :param socket_host: either socket path (unix) or socket IP :type socket_host: str :param socket_port: either string "unix" if it's a unix socket, or port otherwise :type socket_port: str :param openvpn_verb: openvpn verbosity wanted :type openvpn_verb: int :return: A VPN command ready to be launched :rtype: list """ leap_assert(eipconfig, "We need an eip config") leap_assert_type(eipconfig, EIPConfig) leap_assert(providerconfig, "We need a provider config") leap_assert_type(providerconfig, ProviderConfig) leap_assert(socket_host, "We need a socket host!") leap_assert(socket_port, "We need a socket port!") if not self.maybe_kextloaded(): raise EIPNoTunKextLoaded kwargs = {} if ProviderConfig.standalone: kwargs["path_extension"] = os.path.join(providerconfig.get_path_prefix(), "..", "apps", "eip") openvpn_possibilities = which(self.OPENVPN_BIN, **kwargs) if len(openvpn_possibilities) == 0: raise OpenVPNNotFoundException() openvpn = first(openvpn_possibilities) args = [openvpn] args += ["--setenv", "LEAPOPENVPN", "1"] if openvpn_verb is not None: args += ["--verb", "%d" % (openvpn_verb,)] gateways = [] leap_settings = LeapSettings(ProviderConfig.standalone) domain = providerconfig.get_domain() gateway_conf = leap_settings.get_selected_gateway(domain) 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!") raise VPNLauncherException(self.tr("No gateway was found!")) logger.debug("Using gateways ips: {0}".format(", ".join(gateways))) for gw in gateways: args += ["--remote", gw, "1194", "udp"] args += [ "--client", "--dev", "tun", ############################################################## # persist-tun makes ping-restart fail because it leaves a # broken routing table ############################################################## # '--persist-tun', "--persist-key", "--tls-client", "--remote-cert-tls", "server", ] openvpn_configuration = eipconfig.get_openvpn_configuration() for key, value in openvpn_configuration.items(): args += ["--%s" % (key,), value] user = getpass.getuser() ############################################################## # The down-root plugin fails in some situations, so we don't # drop privs for the time being ############################################################## # args += [ # '--user', user, # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name # ] if socket_port == "unix": args += ["--management-client-user", user] args += ["--management-signal", "--management", socket_host, socket_port, "--script-security", "2"] if _has_updown_scripts(self.UP_SCRIPT): args += ["--up", '"%s"' % (self.UP_SCRIPT,)] if _has_updown_scripts(self.DOWN_SCRIPT): args += ["--down", '"%s"' % (self.DOWN_SCRIPT,)] # should have the down script too if _has_updown_scripts(self.OPENVPN_DOWN_PLUGIN): args += [ ########################################################### # For the time being we are disabling the usage of the # down-root plugin, because it doesn't quite work as # expected (i.e. it doesn't run route -del as root # when finishing, so it fails to properly # restart/quit) ########################################################### # '--plugin', self.OPENVPN_DOWN_PLUGIN, # '\'%s\'' % self.DOWN_SCRIPT ] # we set user to be passed to the up/down scripts args += ["--setenv", "LEAPUSER", "%s" % (user,)] args += [ "--cert", eipconfig.get_client_cert_path(providerconfig), "--key", eipconfig.get_client_cert_path(providerconfig), "--ca", providerconfig.get_ca_cert_path(), ] command, cargs = self.get_cocoasudo_ovpn_cmd() cmd_args = cargs + args logger.debug("Running VPN with command:") logger.debug("%s %s" % (command, " ".join(cmd_args))) return [command] + cmd_args
def get_vpn_command( self, eipconfig=None, providerconfig=None, socket_host=None, socket_port="unix", openvpn_verb=1 ): """ Returns the platform dependant vpn launching command. It will look for openvpn in the regular paths and algo in path_prefix/apps/eip/ (in case standalone is set) Might raise: VPNLauncherException, OpenVPNNotFoundException. :param eipconfig: eip configuration object :type eipconfig: EIPConfig :param providerconfig: provider specific configuration :type providerconfig: ProviderConfig :param socket_host: either socket path (unix) or socket IP :type socket_host: str :param socket_port: either string "unix" if it's a unix socket, or port otherwise :type socket_port: str :param openvpn_verb: openvpn verbosity wanted :type openvpn_verb: int :return: A VPN command ready to be launched :rtype: list """ leap_assert(eipconfig, "We need an eip config") leap_assert_type(eipconfig, EIPConfig) leap_assert(providerconfig, "We need a provider config") leap_assert_type(providerconfig, ProviderConfig) leap_assert(socket_host, "We need a socket host!") leap_assert(socket_port, "We need a socket port!") kwargs = {} if ProviderConfig.standalone: kwargs["path_extension"] = os.path.join(providerconfig.get_path_prefix(), "..", "apps", "eip") openvpn_possibilities = which(self.OPENVPN_BIN, **kwargs) if len(openvpn_possibilities) == 0: raise OpenVPNNotFoundException() openvpn = first(openvpn_possibilities) args = [] pkexec = self.maybe_pkexec() if pkexec: args.append(openvpn) openvpn = first(pkexec) args += ["--setenv", "LEAPOPENVPN", "1"] if openvpn_verb is not None: args += ["--verb", "%d" % (openvpn_verb,)] gateways = [] leap_settings = LeapSettings(ProviderConfig.standalone) domain = providerconfig.get_domain() gateway_conf = leap_settings.get_selected_gateway(domain) 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!") raise VPNLauncherException(self.tr("No gateway was found!")) logger.debug("Using gateways ips: {0}".format(", ".join(gateways))) for gw in gateways: args += ["--remote", gw, "1194", "udp"] args += [ "--client", "--dev", "tun", ############################################################## # persist-tun makes ping-restart fail because it leaves a # broken routing table ############################################################## # '--persist-tun', "--persist-key", "--tls-client", "--remote-cert-tls", "server", ] openvpn_configuration = eipconfig.get_openvpn_configuration() for key, value in openvpn_configuration.items(): args += ["--%s" % (key,), value] ############################################################## # The down-root plugin fails in some situations, so we don't # drop privs for the time being ############################################################## # args += [ # '--user', getpass.getuser(), # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name # ] if socket_port == "unix": # that's always the case for linux args += ["--management-client-user", getpass.getuser()] args += ["--management-signal", "--management", socket_host, socket_port, "--script-security", "2"] plugin_path = self.maybe_down_plugin() # If we do not have the down plugin neither in the bundle # nor in the system, we do not do updown scripts. The alternative # is leaving the user without the ability to restore dns and routes # to its original state. if plugin_path and _has_updown_scripts(self.UP_DOWN_PATH): args += [ "--up", self.UP_DOWN_PATH, "--down", self.UP_DOWN_PATH, ############################################################## # For the time being we are disabling the usage of the # down-root plugin, because it doesn't quite work as # expected (i.e. it doesn't run route -del as root # when finishing, so it fails to properly # restart/quit) ############################################################## # '--plugin', plugin_path, # '\'script_type=down %s\'' % self.UP_DOWN_PATH ] args += [ "--cert", eipconfig.get_client_cert_path(providerconfig), "--key", eipconfig.get_client_cert_path(providerconfig), "--ca", providerconfig.get_ca_cert_path(), ] logger.debug("Running VPN with command:") logger.debug("%s %s" % (openvpn, " ".join(args))) return [openvpn] + args
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)
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)
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)
def get_vpn_command(kls, eipconfig, providerconfig, socket_host, socket_port, openvpn_verb=1): """ Returns the platform dependant vpn launching command Might raise: OpenVPNNotFoundException, VPNLauncherException. :param eipconfig: eip configuration object :type eipconfig: EIPConfig :param providerconfig: provider specific configuration :type providerconfig: ProviderConfig :param socket_host: either socket path (unix) or socket IP :type socket_host: str :param socket_port: either string "unix" if it's a unix socket, or port otherwise :type socket_port: str :param openvpn_verb: the openvpn verbosity wanted :type openvpn_verb: int :return: A VPN command ready to be launched. :rtype: list """ leap_assert_type(eipconfig, EIPConfig) leap_assert_type(providerconfig, ProviderConfig) kwargs = {} if flags.STANDALONE: kwargs['path_extension'] = os.path.join( get_path_prefix(), "..", "apps", "eip") openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs) if len(openvpn_possibilities) == 0: raise OpenVPNNotFoundException() openvpn = first(openvpn_possibilities) args = [] args += [ '--setenv', "LEAPOPENVPN", "1", '--nobind' ] if openvpn_verb is not None: args += ['--verb', '%d' % (openvpn_verb,)] gateways = [] leap_settings = LeapSettings() domain = providerconfig.get_domain() gateway_conf = leap_settings.get_selected_gateway(domain) 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!') raise VPNLauncherException('No gateway was found!') logger.debug("Using gateways ips: {0}".format(', '.join(gateways))) for gw in gateways: args += ['--remote', gw, '1194', 'udp'] args += [ '--client', '--dev', 'tun', ############################################################## # persist-tun makes ping-restart fail because it leaves a # broken routing table ############################################################## # '--persist-tun', '--persist-key', '--tls-client', '--remote-cert-tls', 'server' ] openvpn_configuration = eipconfig.get_openvpn_configuration() for key, value in openvpn_configuration.items(): args += ['--%s' % (key,), value] user = getpass.getuser() ############################################################## # The down-root plugin fails in some situations, so we don't # drop privs for the time being ############################################################## # args += [ # '--user', user, # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name # ] if socket_port == "unix": # that's always the case for linux args += [ '--management-client-user', user ] args += [ '--management-signal', '--management', socket_host, socket_port, '--script-security', '2' ] if kls.UP_SCRIPT is not None: if _has_updown_scripts(kls.UP_SCRIPT): args += [ '--up', '\"%s\"' % (kls.UP_SCRIPT,), ] if kls.DOWN_SCRIPT is not None: if _has_updown_scripts(kls.DOWN_SCRIPT): args += [ '--down', '\"%s\"' % (kls.DOWN_SCRIPT,) ] ########################################################### # For the time being we are disabling the usage of the # down-root plugin, because it doesn't quite work as # expected (i.e. it doesn't run route -del as root # when finishing, so it fails to properly # restart/quit) ########################################################### # if _has_updown_scripts(kls.OPENVPN_DOWN_PLUGIN): # args += [ # '--plugin', kls.OPENVPN_DOWN_ROOT, # '\'%s\'' % kls.DOWN_SCRIPT # for OSX # '\'script_type=down %s\'' % kls.DOWN_SCRIPT # for Linux # ] args += [ '--cert', eipconfig.get_client_cert_path(providerconfig), '--key', eipconfig.get_client_cert_path(providerconfig), '--ca', providerconfig.get_ca_cert_path() ] args += [ '--ping', '10', '--ping-restart', '30'] command_and_args = [openvpn] + args return command_and_args