summaryrefslogtreecommitdiff
path: root/src/leap/gui/wizard.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/gui/wizard.py')
-rw-r--r--src/leap/gui/wizard.py626
1 files changed, 0 insertions, 626 deletions
diff --git a/src/leap/gui/wizard.py b/src/leap/gui/wizard.py
deleted file mode 100644
index 2b48fc81..00000000
--- a/src/leap/gui/wizard.py
+++ /dev/null
@@ -1,626 +0,0 @@
-# -*- coding: utf-8 -*-
-# wizard.py
-# Copyright (C) 2013 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-First run wizard
-"""
-import os
-import logging
-import json
-
-from PySide import QtCore, QtGui
-from functools import partial
-from twisted.internet import threads
-
-from ui_wizard import Ui_Wizard
-from leap.config.providerconfig import ProviderConfig
-from leap.crypto.srpregister import SRPRegister
-from leap.util.privilege_policies import is_missing_policy_permissions
-from leap.util.request_helpers import get_content
-from leap.util.keyring_helpers import has_keyring
-from leap.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.services import get_supported
-
-logger = logging.getLogger(__name__)
-
-
-class Wizard(QtGui.QWizard):
- """
- First run wizard to register a user and setup a provider
- """
-
- INTRO_PAGE = 0
- SELECT_PROVIDER_PAGE = 1
- PRESENT_PROVIDER_PAGE = 2
- SETUP_PROVIDER_PAGE = 3
- REGISTER_USER_PAGE = 4
- SERVICES_PAGE = 5
- FINISH_PAGE = 6
-
- WEAK_PASSWORDS = ("123456", "qweasd", "qwerty",
- "password")
-
- BARE_USERNAME_REGEX = r"^[A-Za-z\d_]+$"
-
- def __init__(self, standalone=False, bypass_checks=False):
- """
- Constructor for the main Wizard.
-
- :param standalone: If True, the application is running as standalone
- and the wizard should display some messages according to this.
- :type standalone: bool
- :param bypass_checks: Set to true if the app should bypass
- first round of checks for CA certificates at bootstrap
- :type bypass_checks: bool
- """
- QtGui.QWizard.__init__(self)
-
- self.standalone = standalone
-
- self.ui = Ui_Wizard()
- self.ui.setupUi(self)
-
- self.setPixmap(QtGui.QWizard.LogoPixmap,
- QtGui.QPixmap(":/images/leap-color-small.png"))
-
- self.QUESTION_ICON = QtGui.QPixmap(":/images/Emblem-question.png")
- self.ERROR_ICON = QtGui.QPixmap(":/images/Dialog-error.png")
- self.OK_ICON = QtGui.QPixmap(":/images/Dialog-accept.png")
-
- # Correspondence for services and their name to display
- EIP_LABEL = self.tr("Encrypted Internet")
- MX_LABEL = self.tr("Encrypted Mail")
-
- if self._is_need_eip_password_warning():
- EIP_LABEL += " " + self.tr(
- "(will need admin password to start)")
-
- self.SERVICE_DISPLAY = [
- EIP_LABEL,
- MX_LABEL
- ]
- self.SERVICE_CONFIG = [
- "openvpn",
- "mx"
- ]
-
- self._selected_services = set()
- self._shown_services = set()
-
- self._show_register = False
-
- self.ui.grpCheckProvider.setVisible(False)
- self.ui.btnCheck.clicked.connect(self._check_provider)
- self.ui.lnProvider.returnPressed.connect(self._check_provider)
-
- self._provider_bootstrapper = ProviderBootstrapper(bypass_checks)
- self._provider_bootstrapper.name_resolution.connect(
- self._name_resolution)
- self._provider_bootstrapper.https_connection.connect(
- self._https_connection)
- self._provider_bootstrapper.download_provider_info.connect(
- self._download_provider_info)
-
- self._provider_bootstrapper.download_ca_cert.connect(
- self._download_ca_cert)
- self._provider_bootstrapper.check_ca_fingerprint.connect(
- self._check_ca_fingerprint)
- self._provider_bootstrapper.check_api_certificate.connect(
- self._check_api_certificate)
-
- self._domain = None
- self._provider_config = ProviderConfig()
-
- # We will store a reference to the defers for eventual use
- # (eg, to cancel them) but not doing anything with them right now.
- self._provider_select_defer = None
- self._provider_setup_defer = None
-
- self.currentIdChanged.connect(self._current_id_changed)
-
- self.ui.lblPassword.setEchoMode(QtGui.QLineEdit.Password)
- self.ui.lblPassword2.setEchoMode(QtGui.QLineEdit.Password)
-
- self.ui.lnProvider.textChanged.connect(
- self._enable_check)
-
- self.ui.lblUser.returnPressed.connect(
- self._focus_password)
- self.ui.lblPassword.returnPressed.connect(
- self._focus_second_password)
- self.ui.lblPassword2.returnPressed.connect(
- self._register)
- self.ui.btnRegister.clicked.connect(
- self._register)
-
- usernameRe = QtCore.QRegExp(self.BARE_USERNAME_REGEX)
- self.ui.lblUser.setValidator(
- QtGui.QRegExpValidator(usernameRe, self))
-
- self.page(self.REGISTER_USER_PAGE).setCommitPage(True)
-
- self._username = None
- self._password = None
-
- self.page(self.REGISTER_USER_PAGE).setButtonText(
- QtGui.QWizard.CommitButton, self.tr("&Next >"))
- self.page(self.FINISH_PAGE).setButtonText(
- QtGui.QWizard.FinishButton, self.tr("Connect"))
-
- # XXX: Temporary removal for enrollment policy
- # https://leap.se/code/issues/2922
- self.ui.label_12.setVisible(False)
- self.ui.lblProviderPolicy.setVisible(False)
-
- def get_domain(self):
- return self._domain
-
- def get_username(self):
- return self._username
-
- def get_password(self):
- return self._password
-
- def get_remember(self):
- return has_keyring() and self.ui.chkRemember.isChecked()
-
- def get_services(self):
- return self._selected_services
-
- def _enable_check(self, text):
- self.ui.btnCheck.setEnabled(len(self.ui.lnProvider.text()) != 0)
- self._reset_provider_check()
-
- def _focus_password(self):
- """
- Focuses at the password lineedit for the registration page
- """
- self.ui.lblPassword.setFocus()
-
- def _focus_second_password(self):
- """
- Focuses at the second password lineedit for the registration page
- """
- self.ui.lblPassword2.setFocus()
-
- def _basic_password_checks(self, username, password, password2):
- """
- Performs basic password checks to avoid really easy passwords.
-
- :param username: username provided at the registrarion form
- :type username: str
- :param password: password from the registration form
- :type password: str
- :param password2: second password from the registration form
- :type password: str
-
- :return: returns True if all the checks pass, False otherwise
- :rtype: bool
- """
- message = None
-
- if message is None and password != password2:
- message = self.tr("Passwords don't match")
-
- if message is None and len(password) < 6:
- message = self.tr("Password too short")
-
- if message is None and password in self.WEAK_PASSWORDS:
- message = self.tr("Password too easy")
-
- if message is None and username == password:
- message = self.tr("Password equal to username")
-
- if message is not None:
- self._set_register_status(message, error=True)
- self._focus_password()
- return False
-
- return True
-
- def _register(self):
- """
- Performs the registration based on the values provided in the form
- """
- self.ui.btnRegister.setEnabled(False)
-
- username = self.ui.lblUser.text()
- password = self.ui.lblPassword.text()
- password2 = self.ui.lblPassword2.text()
-
- if self._basic_password_checks(username, password, password2):
- register = SRPRegister(provider_config=self._provider_config)
- register.registration_finished.connect(
- self._registration_finished)
-
- threads.deferToThread(
- partial(register.register_user,
- username.encode("utf8"),
- password.encode("utf8")))
-
- self._username = username
- self._password = password
- self._set_register_status(self.tr("Starting registration..."))
- else:
- self.ui.btnRegister.setEnabled(True)
-
- def _set_registration_fields_visibility(self, visible):
- """
- This method hides the username and password labels and inputboxes.
-
- :param visible: sets the visibility of the widgets
- True: widgets are visible or False: are not
- :type visible: bool
- """
- # username and password inputs
- self.ui.lblUser.setVisible(visible)
- self.ui.lblPassword.setVisible(visible)
- self.ui.lblPassword2.setVisible(visible)
-
- # username and password labels
- self.ui.label_15.setVisible(visible)
- self.ui.label_16.setVisible(visible)
- self.ui.label_17.setVisible(visible)
-
- # register button
- self.ui.btnRegister.setVisible(visible)
-
- def _registration_finished(self, ok, req):
- if ok:
- user_domain = self._username + "@" + self._domain
- message = "<font color='green'><h3>"
- message += self.tr("User %s successfully registered.") % (
- user_domain, )
- message += "</h3></font>"
- self._set_register_status(message)
-
- self.ui.lblPassword2.clearFocus()
- self._set_registration_fields_visibility(False)
-
- # Allow the user to remember his password
- if has_keyring():
- self.ui.chkRemember.setVisible(True)
- self.ui.chkRemember.setEnabled(True)
-
- self.page(self.REGISTER_USER_PAGE).set_completed()
- self.button(QtGui.QWizard.BackButton).setEnabled(False)
- else:
- old_username = self._username
- self._username = None
- self._password = None
- error_msg = self.tr("Unknown error")
- try:
- content, _ = get_content(req)
- json_content = json.loads(content)
- error_msg = json_content.get("errors").get("login")[0]
- if not error_msg.istitle():
- error_msg = "%s %s" % (old_username, error_msg)
- except Exception as e:
- logger.error("Unknown error: %r" % (e,))
-
- self._set_register_status(error_msg, error=True)
- self.ui.btnRegister.setEnabled(True)
-
- def _set_register_status(self, status, error=False):
- """
- Sets the status label in the registration page to status
-
- :param status: status message to display, can be HTML
- :type status: str
- """
- if error:
- status = "<font color='red'><b>%s</b></font>" % (status,)
- self.ui.lblRegisterStatus.setText(status)
-
- def _reset_provider_check(self):
- """
- Resets the UI for checking a provider. Also resets the domain
- in this object.
- """
- self.ui.lblNameResolution.setPixmap(None)
- self.ui.lblHTTPS.setPixmap(None)
- self.ui.lblProviderInfo.setPixmap(None)
- self.ui.lblProviderSelectStatus.setText("")
- self._domain = None
- self.button(QtGui.QWizard.NextButton).setEnabled(False)
- self.page(self.SELECT_PROVIDER_PAGE).set_completed(False)
-
- def _reset_provider_setup(self):
- """
- Resets the UI for setting up a provider.
- """
- self.ui.lblDownloadCaCert.setPixmap(None)
- self.ui.lblCheckCaFpr.setPixmap(None)
- self.ui.lblCheckApiCert.setPixmap(None)
-
- def _check_provider(self):
- """
- SLOT
- TRIGGERS:
- self.ui.btnCheck.clicked
- self.ui.lnProvider.returnPressed
-
- Starts the checks for a given provider
- """
- if len(self.ui.lnProvider.text()) == 0:
- return
-
- self.ui.grpCheckProvider.setVisible(True)
- self.ui.btnCheck.setEnabled(False)
- self.ui.lnProvider.setEnabled(False)
- self.button(QtGui.QWizard.BackButton).clearFocus()
- self._domain = self.ui.lnProvider.text()
-
- self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON)
- self._provider_select_defer = self._provider_bootstrapper.\
- run_provider_select_checks(self._domain)
-
- def _complete_task(self, data, label, complete=False, complete_page=-1):
- """
- Checks a task and completes a page if specified
-
- :param data: data as it comes from the bootstrapper thread for
- a specific check
- :type data: dict
- :param label: label that displays the status icon for a
- specific check that corresponds to the data
- :type label: QtGui.QLabel
- :param complete: if True, it completes the page specified,
- which must be of type WizardPage
- :type complete: bool
- :param complete_page: page id to complete
- :type complete_page: int
- """
- passed = data[self._provider_bootstrapper.PASSED_KEY]
- error = data[self._provider_bootstrapper.ERROR_KEY]
- if passed:
- label.setPixmap(self.OK_ICON)
- if complete:
- self.page(complete_page).set_completed()
- self.button(QtGui.QWizard.NextButton).setFocus()
- else:
- label.setPixmap(self.ERROR_ICON)
- logger.error(error)
-
- def _name_resolution(self, data):
- """
- SLOT
- TRIGGER: self._provider_bootstrapper.name_resolution
-
- Sets the status for the name resolution check
- """
- self._complete_task(data, self.ui.lblNameResolution)
- status = ""
- passed = data[self._provider_bootstrapper.PASSED_KEY]
- if not passed:
- status = self.tr("<font color='red'><b>Non-existent "
- "provider</b></font>")
- else:
- self.ui.lblHTTPS.setPixmap(self.QUESTION_ICON)
- self.ui.lblProviderSelectStatus.setText(status)
- self.ui.btnCheck.setEnabled(not passed)
- self.ui.lnProvider.setEnabled(not passed)
-
- def _https_connection(self, data):
- """
- SLOT
- TRIGGER: self._provider_bootstrapper.https_connection
-
- Sets the status for the https connection check
- """
- self._complete_task(data, self.ui.lblHTTPS)
- status = ""
- passed = data[self._provider_bootstrapper.PASSED_KEY]
- if not passed:
- status = self.tr("<font color='red'><b>%s</b></font>") \
- % (data[self._provider_bootstrapper.ERROR_KEY])
- self.ui.lblProviderSelectStatus.setText(status)
- else:
- self.ui.lblProviderInfo.setPixmap(self.QUESTION_ICON)
- self.ui.btnCheck.setEnabled(not passed)
- self.ui.lnProvider.setEnabled(not passed)
-
- def _download_provider_info(self, data):
- """
- SLOT
- TRIGGER: self._provider_bootstrapper.download_provider_info
-
- Sets the status for the provider information download
- check. Since this check is the last of this set, it also
- completes the page if passed
- """
- if self._provider_config.load(os.path.join("leap",
- "providers",
- self._domain,
- "provider.json")):
- self._complete_task(data, self.ui.lblProviderInfo,
- True, self.SELECT_PROVIDER_PAGE)
- else:
- new_data = {
- self._provider_bootstrapper.PASSED_KEY: False,
- self._provider_bootstrapper.ERROR_KEY:
- self.tr("Unable to load provider configuration")
- }
- self._complete_task(new_data, self.ui.lblProviderInfo)
-
- status = ""
- if not data[self._provider_bootstrapper.PASSED_KEY]:
- status = self.tr("<font color='red'><b>Not a valid provider"
- "</b></font>")
- self.ui.lblProviderSelectStatus.setText(status)
- self.ui.btnCheck.setEnabled(True)
- self.ui.lnProvider.setEnabled(True)
-
- def _download_ca_cert(self, data):
- """
- SLOT
- TRIGGER: self._provider_bootstrapper.download_ca_cert
-
- Sets the status for the download of the CA certificate check
- """
- self._complete_task(data, self.ui.lblDownloadCaCert)
- passed = data[self._provider_bootstrapper.PASSED_KEY]
- if passed:
- self.ui.lblCheckCaFpr.setPixmap(self.QUESTION_ICON)
-
- def _check_ca_fingerprint(self, data):
- """
- SLOT
- TRIGGER: self._provider_bootstrapper.check_ca_fingerprint
-
- Sets the status for the CA fingerprint check
- """
- self._complete_task(data, self.ui.lblCheckCaFpr)
- passed = data[self._provider_bootstrapper.PASSED_KEY]
- if passed:
- self.ui.lblCheckApiCert.setPixmap(self.QUESTION_ICON)
-
- def _check_api_certificate(self, data):
- """
- SLOT
- TRIGGER: self._provider_bootstrapper.check_api_certificate
-
- Sets the status for the API certificate check. Also finishes
- the provider bootstrapper thread since it's not needed anymore
- from this point on, unless the whole check chain is restarted
- """
- self._complete_task(data, self.ui.lblCheckApiCert,
- True, self.SETUP_PROVIDER_PAGE)
-
- 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]))
-
- def _populate_services(self):
- """
- Loads the services that the provider provides into the UI for
- the user to enable or disable.
- """
- self.ui.grpServices.setTitle(
- self.tr("Services by %s") %
- (self._provider_config.get_name(),))
-
- services = get_supported(
- self._provider_config.get_services())
-
- for service in services:
- try:
- if service not in self._shown_services:
- checkbox = QtGui.QCheckBox(self)
- service_index = self.SERVICE_CONFIG.index(service)
- checkbox.setText(self.SERVICE_DISPLAY[service_index])
- self.ui.serviceListLayout.addWidget(checkbox)
- checkbox.stateChanged.connect(
- partial(self._service_selection_changed, service))
- checkbox.setChecked(True)
- self._shown_services.add(service)
- except ValueError:
- logger.error(
- self.tr("Something went wrong while trying to "
- "load service %s" % (service,)))
-
- def _current_id_changed(self, pageId):
- """
- SLOT
- TRIGGER: self.currentIdChanged
-
- Prepares the pages when they appear
- """
- if pageId == self.SELECT_PROVIDER_PAGE:
- self._reset_provider_check()
- self._enable_check("")
-
- if pageId == self.SETUP_PROVIDER_PAGE:
- self._reset_provider_setup()
- self.page(pageId).setSubTitle(self.tr("Gathering configuration "
- "options for %s") %
- (self._provider_config
- .get_name(),))
- self.ui.lblDownloadCaCert.setPixmap(self.QUESTION_ICON)
- self._provider_setup_defer = self._provider_bootstrapper.\
- run_provider_setup_checks(self._provider_config)
-
- if pageId == self.PRESENT_PROVIDER_PAGE:
- self.page(pageId).setSubTitle(self.tr("Description of services "
- "offered by %s") %
- (self._provider_config
- .get_name(),))
-
- lang = QtCore.QLocale.system().name()
- self.ui.lblProviderName.setText(
- "<b>%s</b>" %
- (self._provider_config.get_name(lang=lang),))
- self.ui.lblProviderURL.setText(
- "https://%s" % (self._provider_config.get_domain(),))
- self.ui.lblProviderDesc.setText(
- "<i>%s</i>" %
- (self._provider_config.get_description(lang=lang),))
-
- self.ui.lblServicesOffered.setText(self._provider_config
- .get_services_string())
- self.ui.lblProviderPolicy.setText(self._provider_config
- .get_enrollment_policy())
-
- if pageId == self.REGISTER_USER_PAGE:
- self.page(pageId).setSubTitle(self.tr("Register a new user with "
- "%s") %
- (self._provider_config
- .get_name(),))
- self.ui.chkRemember.setVisible(False)
-
- if pageId == self.SERVICES_PAGE:
- self._populate_services()
-
- def _is_need_eip_password_warning(self):
- """
- Returns True if we need to add a warning about eip needing
- administrative permissions to start. That can be either
- because we are running in standalone mode, or because we could
- not find the needed privilege escalation mechanisms being operative.
- """
- return self.standalone or is_missing_policy_permissions()
-
- def nextId(self):
- """
- Sets the next page id for the wizard based on wether the user
- wants to register a new identity or uses an existing one
- """
- if self.currentPage() == self.page(self.INTRO_PAGE):
- self._show_register = self.ui.rdoRegister.isChecked()
-
- if self.currentPage() == self.page(self.SETUP_PROVIDER_PAGE):
- if self._show_register:
- return self.REGISTER_USER_PAGE
- else:
- return self.SERVICES_PAGE
-
- return QtGui.QWizard.nextId(self)