diff options
author | kali <kali@leap.se> | 2012-11-28 01:11:05 +0900 |
---|---|---|
committer | kali <kali@leap.se> | 2012-11-28 01:11:05 +0900 |
commit | 16f19a225a922dd77f3f6c75c94194ebd229fc67 (patch) | |
tree | d77ba10cdad970b5025e75daa2b3be24e35ce0c7 /src/leap/gui/firstrun/providerselect.py | |
parent | 862014f68fce37318f77309a8f8f9782dabc60d2 (diff) | |
parent | 3ea766452e3c4708c724509d03001c0a0314fcf6 (diff) |
Merge branch 'feature/wizard-usability' into develop
Diffstat (limited to 'src/leap/gui/firstrun/providerselect.py')
-rw-r--r-- | src/leap/gui/firstrun/providerselect.py | 334 |
1 files changed, 304 insertions, 30 deletions
diff --git a/src/leap/gui/firstrun/providerselect.py b/src/leap/gui/firstrun/providerselect.py index 8d1aa869..3ffc6ff6 100644 --- a/src/leap/gui/firstrun/providerselect.py +++ b/src/leap/gui/firstrun/providerselect.py @@ -3,35 +3,69 @@ Select Provider Page, used in First Run Wizard """ import logging +import requests + from PyQt4 import QtCore from PyQt4 import QtGui -#from leap.base import exceptions as baseexceptions +from leap.base import exceptions as baseexceptions #from leap.crypto import certs -#from leap.eip import exceptions as eipexceptions +from leap.eip import exceptions as eipexceptions +from leap.gui.progress import InlineValidationPage +from leap.gui import styles +from leap.util.web import get_https_domain_and_port from leap.gui.constants import APP_LOGO -from leap.gui.styles import ErrorLabelStyleSheet logger = logging.getLogger(__name__) -class SelectProviderPage(QtGui.QWizardPage): +def delay(obj, method_str): + # XXX check newer version in progress.py... + """ + this is a hack to get responsiveness in the ui + """ + QtCore.QTimer().singleShot( + 10, + lambda: QtCore.QMetaObject.invokeMethod( + obj, method_str)) + + +class SelectProviderPage(InlineValidationPage): + + #disableCheckButton = QtCore.pyqtSignal() + launchChecks = QtCore.pyqtSignal() + def __init__(self, parent=None, providers=None): super(SelectProviderPage, self).__init__(parent) + self.current_page = 'providerselection' - self.setTitle("Enter Provider") - self.setSubTitle( + self.setTitle(self.tr("Enter Provider")) + self.setSubTitle(self.tr( "Please enter the domain of the provider you want " - "to use for your connection." + "to use for your connection.") ) self.setPixmap( QtGui.QWizard.LogoPixmap, QtGui.QPixmap(APP_LOGO)) self.did_cert_check = False - self.current_page = 'providerselection' + self.is_done = False + + self.setupSteps() + self.setupUI() + + self.launchChecks.connect( + self.launch_checks) + + self.providerNameEdit.editingFinished.connect( + lambda: self.providerCheckButton.setFocus(True)) + + def setupUI(self): + """ + initializes the UI + """ providerNameLabel = QtGui.QLabel("h&ttps://") # note that we expect the bare domain name # we will add the scheme later @@ -59,8 +93,10 @@ class SelectProviderPage(QtGui.QWizardPage): #self.registerField('provider_name_index', providerNameSelect) validationMsg = QtGui.QLabel("") - validationMsg.setStyleSheet(ErrorLabelStyleSheet) + validationMsg.setStyleSheet(styles.ErrorLabelStyleSheet) self.validationMsg = validationMsg + providerCheckButton = QtGui.QPushButton(self.tr("chec&k!")) + self.providerCheckButton = providerCheckButton # cert info @@ -69,7 +105,8 @@ class SelectProviderPage(QtGui.QWizardPage): # tricky, since the first time came # from the exception message. # should get string from exception too! - self.bad_cert_status = "Server certificate could not be verified." + self.bad_cert_status = self.tr( + "Server certificate could not be verified.") self.certInfo = QtGui.QLabel("") self.certInfo.setWordWrap(True) @@ -81,25 +118,226 @@ class SelectProviderPage(QtGui.QWizardPage): self.onTrustCheckChanged) self.providerNameEdit.textChanged.connect( self.onProviderChanged) + self.providerCheckButton.clicked.connect( + self.onCheckButtonClicked) layout = QtGui.QGridLayout() layout.addWidget(validationMsg, 0, 2) layout.addWidget(providerNameLabel, 1, 1) layout.addWidget(providerNameEdit, 1, 2) + layout.addWidget(providerCheckButton, 1, 3) + + # add certinfo group + # XXX not shown now. should move to validation box. + #layout.addWidget(certinfoGroup, 4, 1, 4, 2) + #self.certinfoGroup = certinfoGroup + #self.certinfoGroup.hide() + + # add validation frame + self.setupValidationFrame() + layout.addWidget(self.valFrame, 4, 2, 4, 2) + self.valFrame.hide() - # XXX get a groupbox or something.... - certinfoGroup = QtGui.QGroupBox("Certificate validation") + self.setLayout(layout) + + # certinfo + + def setupCertInfoGroup(self): + # XXX not used now. + certinfoGroup = QtGui.QGroupBox( + self.tr("Certificate validation")) certinfoLayout = QtGui.QVBoxLayout() certinfoLayout.addWidget(self.certInfo) certinfoLayout.addWidget(self.certWarning) certinfoLayout.addWidget(self.trustProviderCertCheckBox) certinfoGroup.setLayout(certinfoLayout) + self.certinfoGroup = self.certinfoGroup - layout.addWidget(certinfoGroup, 4, 1, 4, 2) - self.certinfoGroup = certinfoGroup - self.certinfoGroup.hide() + # progress frame - self.setLayout(layout) + def setupValidationFrame(self): + qframe = QtGui.QFrame + valFrame = qframe() + valFrame.setFrameStyle(qframe.NoFrame) + valframeLayout = QtGui.QVBoxLayout() + zeros = (0, 0, 0, 0) + valframeLayout.setContentsMargins(*zeros) + + valframeLayout.addWidget(self.stepsTableWidget) + valFrame.setLayout(valframeLayout) + self.valFrame = valFrame + + @QtCore.pyqtSlot() + def onDisableCheckButton(self): + #print 'CHECK BUTTON DISABLED!!!' + self.providerCheckButton.setDisabled(True) + + @QtCore.pyqtSlot() + def launch_checks(self): + self.do_checks() + + def onCheckButtonClicked(self): + QtCore.QMetaObject.invokeMethod( + self, "onDisableCheckButton") + + QtCore.QMetaObject.invokeMethod( + self, "showStepsFrame") + + # is this still needed? + # XXX can I doo delay(self, "do_checks") ? + delay(self, "launch_checks") + + def _do_checks(self): + """ + generator that yields actual checks + that are executed in a separate thread + """ + + wizard = self.wizard() + full_domain = self.providerNameEdit.text() + + # we check if we have a port in the domain string. + domain, port = get_https_domain_and_port(full_domain) + _domain = u"%s:%s" % (domain, port) if port != 443 else unicode(domain) + + netchecker = wizard.netchecker() + + providercertchecker = wizard.providercertchecker() + eipconfigchecker = wizard.eipconfigchecker(domain=_domain) + + yield(("head_sentinel", 0), lambda: None) + + ######################## + # 1) try name resolution + ######################## + + def namecheck(): + """ + in which we check if + we are able to name resolve + this domain + """ + try: + netchecker.check_name_resolution( + domain) + + except baseexceptions.LeapException as exc: + logger.error(exc.message) + return self.fail(exc.usermessage) + + except Exception as exc: + return self.fail(exc.message) + + else: + return True + + logger.debug('checking name resolution') + yield((self.tr("checking domain name"), 20), namecheck) + + ######################### + # 2) try https connection + ######################### + + def httpscheck(): + """ + in which we check + if the provider + is offering service over + https + """ + try: + providercertchecker.is_https_working( + "https://%s" % _domain, + verify=True) + + except eipexceptions.HttpsBadCertError as exc: + logger.debug('exception') + return self.fail(exc.usermessage) + # XXX skipping for now... + ############################################## + # We had this validation logic + # in the provider selection page before + ############################################## + #if self.trustProviderCertCheckBox.isChecked(): + #pass + #else: + #fingerprint = certs.get_cert_fingerprint( + #domain=domain, sep=" ") + + # it's ok if we've trusted this fgprt before + #trustedcrts = wizard.trusted_certs + #if trustedcrts and \ + # fingerprint.replace(' ', '') in trustedcrts: + #pass + #else: + # let your user face panick :P + #self.add_cert_info(fingerprint) + #self.did_cert_check = True + #self.completeChanged.emit() + #return False + + except baseexceptions.LeapException as exc: + return self.fail(exc.usermessage) + + except Exception as exc: + return self.fail(exc.message) + + else: + return True + + logger.debug('checking https connection') + yield((self.tr("checking https connection"), 40), httpscheck) + + ################################## + # 3) try download provider info... + ################################## + + def fetchinfo(): + try: + # XXX we already set _domain in the initialization + # so it should not be needed here. + eipconfigchecker.fetch_definition(domain=_domain) + wizard.set_providerconfig( + eipconfigchecker.defaultprovider.config) + except requests.exceptions.SSLError: + # XXX we should have catched this before. + # but cert checking is broken. + return self.fail(self.tr( + "Could not get info from provider.")) + except requests.exceptions.ConnectionError: + return self.fail(self.tr( + "Could not download provider info " + "(refused conn.).")) + + except Exception as exc: + return self.fail( + self.tr(exc.message)) + else: + return True + + yield((self.tr("fetching provider info"), 80), fetchinfo) + + # done! + + self.is_done = True + yield(("end_sentinel", 100), lambda: None) + + def on_checks_validation_ready(self): + """ + called after _do_checks has finished. + """ + self.domain_checked = True + self.completeChanged.emit() + # let's set focus... + if self.is_done: + self.wizard().clean_validation_error(self.current_page) + nextbutton = self.wizard().button(QtGui.QWizard.NextButton) + nextbutton.setFocus() + else: + self.providerNameEdit.setFocus() + + # cert trust verification + # (disabled for now) def is_insecure_cert_trusted(self): return self.trustProviderCertCheckBox.isChecked() @@ -117,38 +355,50 @@ class SelectProviderPage(QtGui.QWizardPage): # trigger signal to redraw next button self.completeChanged.emit() + def add_cert_info(self, certinfo): + self.certWarning.setText( + "Do you want to <b>trust this provider certificate?</b>") + self.certInfo.setText( + 'SHA-256 fingerprint: <i>%s</i><br>' % certinfo) + self.certInfo.setWordWrap(True) + self.certinfoGroup.show() + def onProviderChanged(self, text): + self.is_done = False + provider = self.providerNameEdit.text() + if provider: + self.providerCheckButton.setDisabled(False) + else: + self.providerCheckButton.setDisabled(True) self.completeChanged.emit() def reset_validation_status(self): """ empty the validation msg + and clean the inline validation widget. """ self.validationMsg.setText('') - - #def set_validation_status(selF, STATUS): - #self.validationMsg.setText(status) - - def add_cert_info(self, certinfo): - self.certWarning.setText( - "Do you want to <b>trust this provider certificate?</b>") - self.certInfo.setText( - 'SHA-256 fingerprint: <i>%s</i><br>' % certinfo) - self.certInfo.setWordWrap(True) - self.certinfoGroup.show() + self.steps.removeAllSteps() + self.clearTable() + self.domain_checked = False # pagewizard methods def isComplete(self): provider = self.providerNameEdit.text() + if not self.is_done: + return False + if not provider: return False else: if self.is_insecure_cert_trusted(): return True if not self.did_cert_check: - return True + if self.is_done: + # XXX sure? + return True return False def populateErrors(self): @@ -163,17 +413,33 @@ class SelectProviderPage(QtGui.QWizardPage): bad_str = getattr(self, 'bad_string', None) cur_str = self.providerNameEdit.text() showerr = self.validationMsg.setText + markred = lambda: self.providerNameEdit.setStyleSheet( + styles.ErrorLineEdit) + umarkrd = lambda: self.providerNameEdit.setStyleSheet( + styles.RegularLineEdit) if bad_str is None: # first time we fall here. # save the current bad_string value self.bad_string = cur_str showerr(errors) + markred() else: # not the first time + # XXX hey, this is getting convoluted. + # roll out this. + # but be careful about all the possibilities + # with going back and forth once you + # enter a domain. if cur_str == bad_str: showerr(errors) + markred() else: - showerr('') + if not getattr(self, 'domain_checked', None): + showerr('') + umarkrd() + else: + self.bad_string = cur_str + showerr(errors) def cleanup_errormsg(self): """ @@ -181,6 +447,7 @@ class SelectProviderPage(QtGui.QWizardPage): should be called before leaving the page """ self.bad_string = None + self.domain_checked = False def paintEvent(self, event): """ @@ -195,7 +462,14 @@ class SelectProviderPage(QtGui.QWizardPage): def initializePage(self): self.validationMsg.setText('') - self.certinfoGroup.hide() + if hasattr(self, 'certinfoGroup'): + # XXX remove ? + self.certinfoGroup.hide() + self.is_done = False + self.providerCheckButton.setDisabled(True) + self.valFrame.hide() + self.steps.removeAllSteps() + self.clearTable() def validatePage(self): # some cleanup before we leave the page |