diff options
| -rw-r--r-- | README.rst | 14 | ||||
| -rw-r--r-- | pkg/requirements.pip | 3 | ||||
| -rw-r--r-- | src/leap/app.py | 9 | ||||
| -rw-r--r-- | src/leap/base/config.py | 1 | ||||
| -rw-r--r-- | src/leap/baseapp/leap_app.py | 54 | ||||
| -rw-r--r-- | src/leap/baseapp/mainwindow.py | 32 | ||||
| -rw-r--r-- | src/leap/crypto/__init__.py | 0 | ||||
| -rw-r--r-- | src/leap/crypto/leapkeyring.py | 64 | ||||
| -rw-r--r-- | src/leap/eip/checks.py | 3 | ||||
| -rwxr-xr-x | src/leap/gui/firstrunwizard.py | 489 | ||||
| -rw-r--r-- | src/leap/gui/tests/integration/fake_user_signup.py | 80 | 
11 files changed, 742 insertions, 7 deletions
| @@ -14,11 +14,20 @@ Listed in pkg/requirements.pip and pkg/test-requirements.pip  * qt4 libraries  * python-qt4  * python-gnutls == 1.1.9 +* python-keyring  * python-nose, python-mock, python-coverage (if you want to run tests)  If you are on a debian-based system, you can run: -apt-get install python-qt4 python-qt4-doc pyqt4-dev-tools python-gnutls libgnutls-dev python-setuptools python-nose +  apt-get install python-qt4 python-qt4-doc pyqt4-dev-tools python-gnutls libgnutls-dev python-setuptools python-nose + +# **note**: I think setuptools is needed for build process only. +# we should separate what's needed as a lib dependency, and what's a dependency that has been debianized but +# still can be retrieved using pip. + +If you are installing in a virtualenv: + +  pip install -r pkg/requirements.pip  # **note**: I _think_ setuptools is needed for build process only.                       # we should separate what's needed as a global lib dependency, and what's a dependency that @@ -62,7 +71,7 @@ python setup.py install # as root if installing globally.  Running the App  ----------------- -If you're running a branded build, the script name will have a suffix that +If you're running a branded build, the script name will have a infix that  depends on your build flavor. Look for it in /usr/local/bin  % leap-springbok-client @@ -81,6 +90,7 @@ Development  Hack  -------------- +  Some steps to be run when setting a development environment for the first time.  # recommended: enable a virtualenv to isolate your libraries. diff --git a/pkg/requirements.pip b/pkg/requirements.pip index c5eefcd4..2406884d 100644 --- a/pkg/requirements.pip +++ b/pkg/requirements.pip @@ -5,3 +5,6 @@ psutil  netifaces  python-gnutls==1.1.9  # see https://bugs.launchpad.net/ubuntu/+source/python-gnutls/+bug/1027129  jsonschema +srp +pycrypto +keyring diff --git a/src/leap/app.py b/src/leap/app.py index 3170de4a..341f6a6e 100644 --- a/src/leap/app.py +++ b/src/leap/app.py @@ -3,6 +3,7 @@ import logging  # This is only needed for Python v2 but is harmless for Python v3.  import sip  sip.setapi('QVariant', 2) +sip.setapi('QString', 2)  from PyQt4.QtGui import (QApplication, QSystemTrayIcon, QMessageBox)  from leap import __version__ as VERSION @@ -50,6 +51,14 @@ def main():      logger.info('Starting app')      app = QApplication(sys.argv) +    # needed for initializing qsettings +    # it will write .config/leap/leap.conf +    # top level app settings +    # in a platform independent way +    app.setOrganizationName("leap") +    app.setApplicationName("leap") +    app.setOrganizationDomain("leap.se") +      if not QSystemTrayIcon.isSystemTrayAvailable():          QMessageBox.critical(None, "Systray",                               "I couldn't detect" diff --git a/src/leap/base/config.py b/src/leap/base/config.py index dc047f80..57f9f1b7 100644 --- a/src/leap/base/config.py +++ b/src/leap/base/config.py @@ -149,6 +149,7 @@ class JSONLeapConfig(BaseLeapConfig):          if not fetcher:              fetcher = self.fetcher          logger.debug('verify: %s', verify) +        logger.debug('uri: %s', uri)          request = fetcher.get(uri, verify=verify)          # XXX should send a if-modified-since header diff --git a/src/leap/baseapp/leap_app.py b/src/leap/baseapp/leap_app.py index 98ca292e..460d1269 100644 --- a/src/leap/baseapp/leap_app.py +++ b/src/leap/baseapp/leap_app.py @@ -1,5 +1,9 @@  import logging +import sip +sip.setapi('QVariant', 2) + +from PyQt4 import QtCore  from PyQt4 import QtGui  from leap.gui import mainwindow_rc @@ -23,9 +27,9 @@ class MainWindowMixin(object):          widget = QtGui.QWidget()          self.setCentralWidget(widget) +        mainLayout = QtGui.QVBoxLayout()          # add widgets to layout          #self.createWindowHeader() -        mainLayout = QtGui.QVBoxLayout()          #mainLayout.addWidget(self.headerBox)          mainLayout.addWidget(self.statusIconBox)          if self.debugmode: @@ -33,11 +37,51 @@ class MainWindowMixin(object):              mainLayout.addWidget(self.loggerBox)          widget.setLayout(mainLayout) +        self.createMainActions() +        self.createMainMenus() +          self.setWindowTitle("LEAP Client")          self.set_app_icon() -        self.resize(400, 300)          self.set_statusbarMessage('ready') -        logger.debug('set ready.........') + +    def createMainActions(self): +        #self.openAct = QtGui.QAction("&Open...", self, shortcut="Ctrl+O", +                #triggered=self.open) + +        self.firstRunWizardAct = QtGui.QAction( +            "&First run wizard...", self, +            triggered=self.launch_first_run_wizard) +        self.aboutAct = QtGui.QAction("&About", self, triggered=self.about) + +        #self.aboutQtAct = QtGui.QAction("About &Qt", self, +                #triggered=QtGui.qApp.aboutQt) + +    def createMainMenus(self): +        self.connMenu = QtGui.QMenu("&Connections", self) +        #self.viewMenu.addSeparator() +        self.connMenu.addAction(self.quitAction) + +        self.settingsMenu = QtGui.QMenu("&Settings", self) +        self.settingsMenu.addAction(self.firstRunWizardAct) + +        self.helpMenu = QtGui.QMenu("&Help", self) +        self.helpMenu.addAction(self.aboutAct) +        #self.helpMenu.addAction(self.aboutQtAct) + +        self.menuBar().addMenu(self.connMenu) +        self.menuBar().addMenu(self.settingsMenu) +        self.menuBar().addMenu(self.helpMenu) + +    def launch_first_run_wizard(self): +        settings = QtCore.QSettings() +        settings.setValue('FirstRunWizardDone', False) +        logger.debug('should run first run wizard again...') + +        from leap.gui.firstrunwizard import FirstRunWizard +        wizard = FirstRunWizard( +            parent=self, +            success_cb=self.initReady.emit) +        wizard.show()      def set_app_icon(self):          icon = QtGui.QIcon(APP_LOGO) @@ -88,6 +132,10 @@ class MainWindowMixin(object):          """          cleans state before shutting down app.          """ +        # save geometry for restoring +        settings = QtCore.QSettings() +        settings.setValue("Geometry", self.saveGeometry()) +          # TODO:make sure to shutdown all child process / threads          # in conductor          # XXX send signal instead? diff --git a/src/leap/baseapp/mainwindow.py b/src/leap/baseapp/mainwindow.py index 55be55f7..1accac30 100644 --- a/src/leap/baseapp/mainwindow.py +++ b/src/leap/baseapp/mainwindow.py @@ -26,18 +26,26 @@ class LeapWindow(QtGui.QMainWindow,      newLogLine = QtCore.pyqtSignal([str])      statusChange = QtCore.pyqtSignal([object]) +    mainappReady = QtCore.pyqtSignal([]) +    initReady = QtCore.pyqtSignal([])      def __init__(self, opts):          logger.debug('init leap window')          self.debugmode = getattr(opts, 'debug', False) -          super(LeapWindow, self).__init__()          if self.debugmode:              self.createLogBrowser() +          EIPConductorAppMixin.__init__(self, opts=opts)          StatusAwareTrayIconMixin.__init__(self)          MainWindowMixin.__init__(self) +        settings = QtCore.QSettings() +        geom = settings.value("Geometry") +        if geom: +            self.restoreGeometry(geom) +        self.wizard_done = settings.value("FirstRunWizardDone") +          self.initchecks = InitChecksThread(self.run_eip_checks)          # bind signals @@ -51,8 +59,28 @@ class LeapWindow(QtGui.QMainWindow,          self.timer.timeout.connect(              lambda: self.onTimerTick()) -        # ... all ready. go! +        # do frwizard and init signals +        self.mainappReady.connect(self.do_first_run_wizard_check) +        self.initReady.connect(self.runchecks_and_eipconnect) +        # ... all ready. go! +        # calls do_first_run_wizard_check +        self.mainappReady.emit() + +    def do_first_run_wizard_check(self): +        logger.debug('first run wizard check...') +        if self.wizard_done: +            self.initReady.emit() +        else: +            # need to run first-run-wizard +            logger.debug('running first run wizard') +            from leap.gui.firstrunwizard import FirstRunWizard +            wizard = FirstRunWizard( +                parent=self, +                success_cb=self.initReady.emit) +            wizard.show() + +    def runchecks_and_eipconnect(self):          self.initchecks.begin() diff --git a/src/leap/crypto/__init__.py b/src/leap/crypto/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/leap/crypto/__init__.py diff --git a/src/leap/crypto/leapkeyring.py b/src/leap/crypto/leapkeyring.py new file mode 100644 index 00000000..bb0ca147 --- /dev/null +++ b/src/leap/crypto/leapkeyring.py @@ -0,0 +1,64 @@ +import os + +import keyring + +############# +# Disclaimer +############# +# This currently is not a keyring, it's more like a joke. +# No, seriously. +# We're affected by this **bug** + +# https://bitbucket.org/kang/python-keyring-lib/ +# issue/65/dbusexception-method-opensession-with + +# so using the gnome keyring does not seem feasible right now. +# I thought this was the next best option to store secrets in plain sight. + +# in the future we should move to use the gnome/kde/macosx/win keyrings. + + +class LeapCryptedFileKeyring(keyring.backend.CryptedFileKeyring): + +    filename = os.path.expanduser("~/.config/leap/.secrets") + +    def __init__(self, seed=None): +        self.seed = seed + +    def _get_new_password(self): +        # XXX every time this method is called, +        # $deity kills a kitten. +        return "secret%s" % self.seed + +    def _init_file(self): +        self.keyring_key = self._get_new_password() +        self.set_password('keyring_setting', 'pass_ref', 'pass_ref_value') + +    def _unlock(self): +        self.keyring_key = self._get_new_password() +        print 'keyring key ', self.keyring_key +        try: +            ref_pw = self.get_password( +                'keyring_setting', +                'pass_ref') +            print 'ref pw ', ref_pw +            assert ref_pw == "pass_ref_value" +        except AssertionError: +            self._lock() +            raise ValueError('Incorrect password') + + +def leap_set_password(key, value, seed="xxx"): +    keyring.set_keyring(LeapCryptedFileKeyring(seed=seed)) +    keyring.set_password('leap', key, value) + + +def leap_get_password(key, seed="xxx"): +    keyring.set_keyring(LeapCryptedFileKeyring(seed=seed)) +    return keyring.get_password('leap', key) + + +if __name__ == "__main__": +    leap_set_password('test', 'bar') +    passwd = leap_get_password('test') +    assert passwd == 'bar' diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index f79d47f5..413a3467 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -232,6 +232,9 @@ class ProviderCertChecker(object):              # verify=verify              # Workaround for #638. return to verification              # when That's done!!! + +            # XXX HOOK SRP here... +            # will have to be more generic in the future.              req = self.fetcher.get(uri, verify=False)              req.raise_for_status()          except requests.exceptions.SSLError: diff --git a/src/leap/gui/firstrunwizard.py b/src/leap/gui/firstrunwizard.py new file mode 100755 index 00000000..abdff7cf --- /dev/null +++ b/src/leap/gui/firstrunwizard.py @@ -0,0 +1,489 @@ +#!/usr/bin/env python +import logging +import json +import socket + +import sip +sip.setapi('QString', 2) +sip.setapi('QVariant', 2) + +from PyQt4 import QtCore +from PyQt4 import QtGui + +from leap.crypto import leapkeyring +from leap.gui import mainwindow_rc + +logger = logging.getLogger(__name__) + +APP_LOGO = ':/images/leap-color-small.png' + +# registration ###################### +# move to base/ +import binascii + +import requests +import srp + +from leap.base import constants as baseconstants + +SIGNUP_TIMEOUT = getattr(baseconstants, 'SIGNUP_TIMEOUT', 5) + + +class LeapSRPRegister(object): + +    def __init__(self, +                 schema="https", +                 provider=None, +                 port=None, +                 register_path="1/users.json", +                 method="POST", +                 fetcher=requests, +                 srp=srp, +                 hashfun=srp.SHA256, +                 ng_constant=srp.NG_1024): + +        self.schema = schema +        self.provider = provider +        self.port = port +        self.register_path = register_path +        self.method = method +        self.fetcher = fetcher +        self.srp = srp +        self.HASHFUN = hashfun +        self.NG = ng_constant + +        self.init_session() + +    def init_session(self): +        self.session = self.fetcher.session() + +    def get_registration_uri(self): +        # XXX assert is https! +        # use urlparse +        if self.port: +            uri = "%s://%s:%s/%s" % ( +                self.schema, +                self.provider, +                self.port, +                self.register_path) +        else: +            uri = "%s://%s/%s" % ( +                self.schema, +                self.provider, +                self.register_path) + +        return uri + +    def register_user(self, username, password, keep=False): +        """ +        @rtype: tuple +        @rvalue: (ok, request) +        """ +        salt, vkey = self.srp.create_salted_verification_key( +            username, +            password, +            self.HASHFUN, +            self.NG) + +        user_data = { +            'user[login]': username, +            'user[password_verifier]': binascii.hexlify(vkey), +            'user[password_salt]': binascii.hexlify(salt)} + +        uri = self.get_registration_uri() +        logger.debug('post to uri: %s' % uri) + +        # XXX get self.method +        req = self.session.post( +            uri, data=user_data, +            timeout=SIGNUP_TIMEOUT) +        logger.debug(req) +        logger.debug('user_data: %s', user_data) +        #logger.debug('response: %s', req.text) +        # we catch it in the form +        #req.raise_for_status() +        return (req.ok, req) + +###################################### + +ErrorLabelStyleSheet = """ +QLabel { color: red; +         font-weight: bold} +""" + + +class FirstRunWizard(QtGui.QWizard): + +    def __init__( +            self, parent=None, providers=None, +            success_cb=None): +        super(FirstRunWizard, self).__init__( +            parent, +            QtCore.Qt.WindowStaysOnTopHint) + +        # XXX hardcoded for tests +        if not providers: +            providers = ('springbok',) +        self.providers = providers + +        # success callback +        self.success_cb = success_cb + +        self.addPage(IntroPage()) +        self.addPage(SelectProviderPage(providers=providers)) + +        self.addPage(RegisterUserPage(wizard=self)) +        #self.addPage(GlobalEIPSettings()) +        self.addPage(LastPage()) + +        self.setPixmap( +            QtGui.QWizard.BannerPixmap, +            QtGui.QPixmap(':/images/banner.png')) +        self.setPixmap( +            QtGui.QWizard.BackgroundPixmap, +            QtGui.QPixmap(':/images/background.png')) + +        self.setWindowTitle("First Run Wizard") + +        # TODO: set style for MAC / windows ... +        #self.setWizardStyle() + +    def setWindowFlags(self, flags): +        logger.debug('setting window flags') +        QtGui.QWizard.setWindowFlags(self, flags) + +    def focusOutEvent(self, event): +        # needed ? +        self.setFocus(True) +        self.activateWindow() +        self.raise_() +        self.show() + +    def accept(self): +        """ +        final step in the wizard. +        gather the info, update settings +        and call the success callback. +        """ +        provider = self.get_provider() +        username = self.field('userName') +        password = self.field('userPassword') +        remember_pass = self.field('rememberPassword') + +        logger.debug('chosen provider: %s', provider) +        logger.debug('username: %s', username) +        logger.debug('remember password: %s', remember_pass) +        super(FirstRunWizard, self).accept() + +        settings = QtCore.QSettings() +        settings.setValue("FirstRunWizardDone", True) +        settings.setValue( +            "eip_%s_username" % provider, +            username) +        settings.setValue("%s_remember_pass" % provider, remember_pass) + +        seed = self.get_random_str(10) +        settings.setValue("%s_seed" % provider, seed) + +        leapkeyring.leap_set_password(username, password, seed=seed) + +        logger.debug('First Run Wizard Done.') +        cb = self.success_cb +        if cb and callable(cb): +            self.success_cb() + +    def get_provider(self): +        provider = self.field('provider_index') +        return self.providers[provider] + +    def get_random_str(self, n): +        from string import (ascii_uppercase, ascii_lowercase, digits) +        from random import choice +        return ''.join(choice( +            ascii_uppercase + +            ascii_lowercase + +            digits) for x in range(n)) + + +class IntroPage(QtGui.QWizardPage): +    def __init__(self, parent=None): +        super(IntroPage, self).__init__(parent) + +        self.setTitle("First run wizard.") + +        #self.setPixmap( +            #QtGui.QWizard.WatermarkPixmap, +            #QtGui.QPixmap(':/images/watermark1.png')) + +        label = QtGui.QLabel( +            "Now we will guide you through " +            "some configuration that is needed before you " +            "can connect for the first time.<br><br>" +            "If you ever need to modify these options again, " +            "you can find the wizard in the '<i>Settings</i>' menu from the " +            "main window of the Leap App.") + +        label.setWordWrap(True) + +        layout = QtGui.QVBoxLayout() +        layout.addWidget(label) +        self.setLayout(layout) + + +class SelectProviderPage(QtGui.QWizardPage): +    def __init__(self, parent=None, providers=None): +        super(SelectProviderPage, self).__init__(parent) + +        self.setTitle("Select Provider") +        self.setSubTitle( +            "Please select which provider do you want " +            "to use for your connection." +        ) +        self.setPixmap( +            QtGui.QWizard.LogoPixmap, +            QtGui.QPixmap(APP_LOGO)) + +        providerNameLabel = QtGui.QLabel("&Provider:") + +        providercombo = QtGui.QComboBox() +        if providers: +            for provider in providers: +                providercombo.addItem(provider) +        providerNameSelect = providercombo + +        providerNameLabel.setBuddy(providerNameSelect) + +        self.registerField('provider_index', providerNameSelect) + +        layout = QtGui.QGridLayout() +        layout.addWidget(providerNameLabel, 0, 0) +        layout.addWidget(providerNameSelect, 0, 1) +        self.setLayout(layout) + + +class RegisterUserPage(QtGui.QWizardPage): +    setSigningUpStatus = QtCore.pyqtSignal([]) + +    def __init__(self, parent=None, wizard=None): +        super(RegisterUserPage, self).__init__(parent) + +        # bind wizard page signals +        self.setSigningUpStatus.connect( +            self.set_status_validating) + +        # XXX check for no wizard pased +        # getting provider from previous step +        provider = wizard.get_provider() + +        self.setTitle("User registration") +        self.setSubTitle( +            "Register a new user with provider %s." % +            provider) +        self.setPixmap( +            QtGui.QWizard.LogoPixmap, +            QtGui.QPixmap(APP_LOGO)) + +        rememberPasswordCheckBox = QtGui.QCheckBox( +            "&Remember password.") +        rememberPasswordCheckBox.setChecked(True) + +        userNameLabel = QtGui.QLabel("User &name:") +        userNameLineEdit = QtGui.QLineEdit() +        userNameLineEdit.cursorPositionChanged.connect( +            self.reset_validation_status) +        userNameLabel.setBuddy(userNameLineEdit) + +        # add regex validator +        usernameRe = QtCore.QRegExp(r"^[A-Za-z\d_]+$") +        userNameLineEdit.setValidator( +            QtGui.QRegExpValidator(usernameRe, self)) +        self.userNameLineEdit = userNameLineEdit + +        userPasswordLabel = QtGui.QLabel("&Password:") +        self.userPasswordLineEdit = QtGui.QLineEdit() +        self.userPasswordLineEdit.setEchoMode( +            QtGui.QLineEdit.Password) + +        userPasswordLabel.setBuddy(self.userPasswordLineEdit) + +        self.registerField('userName', self.userNameLineEdit) +        self.registerField('userPassword', self.userPasswordLineEdit) +        self.registerField('rememberPassword', rememberPasswordCheckBox) + +        layout = QtGui.QGridLayout() +        layout.setColumnMinimumWidth(0, 20) + +        validationMsg = QtGui.QLabel("") +        validationMsg.setStyleSheet(ErrorLabelStyleSheet) + +        self.validationMsg = validationMsg + +        layout.addWidget(validationMsg, 0, 3) + +        layout.addWidget(userNameLabel, 1, 0) +        layout.addWidget(self.userNameLineEdit, 1, 3) + +        layout.addWidget(userPasswordLabel, 2, 0) +        layout.addWidget(self.userPasswordLineEdit, 2, 3) + +        layout.addWidget(rememberPasswordCheckBox, 3, 3, 3, 4) +        self.setLayout(layout) + +    def reset_validation_status(self): +        """ +        empty the validation msg +        """ +        self.validationMsg.setText('') + +    def set_status_validating(self): +        """ +        set validation msg to 'registering...' +        """ +        # XXX  this is NOT WORKING. +        # My guess is that, even if we are using +        # signals to trigger this, it does +        # not show until the validate function +        # returns. +        # I guess it is because there is no delay... +        logger.debug('registering........') +        self.validationMsg.setText('registering...') +        # need to call update somehow??? + +    def set_status_invalid_username(self): +        """ +        set validation msg to +        not available user +        """ +        self.validationMsg.setText('Username not available.') + +    def set_status_server_500(self): +        """ +        set validation msg to +        internal server error +        """ +        self.validationMsg.setText("Error during registration (500)") + +    def set_status_timeout(self): +        """ +        set validation msg to +        timeout +        """ +        self.validationMsg.setText("Error connecting to provider (timeout)") + +    def set_status_unknown_error(self): +        """ +        set validation msg to +        unknown error +        """ +        self.validationMsg.setText("Error during signup") + +    # overwritten methods + +    def initializePage(self): +        """ +        inits wizard page +        """ +        self.validationMsg.setText('') + +    def validatePage(self): +        """ +        validation +        we initialize the srp protocol register +        and try to register user. if error +        returned we write validation error msg +        above the form. +        """ +        # the slot for this signal is not doing +        # what's expected. Investigate why, +        # right now we're not giving any feedback +        # to the user re. what's going on. The only +        # thing I can see as a workaround is setting +        # a low timeout. +        self.setSigningUpStatus.emit() + +        username = self.userNameLineEdit.text() +        password = self.userPasswordLineEdit.text() + +        # XXX TODO -- remove debug info +        # XXX get from provider info +        # XXX enforce https +        # and pass a verify value + +        signup = LeapSRPRegister( +            schema="http", +            provider="springbok", + +            #provider="localhost", +            #register_path="timeout", +            #port=8000 +        ) +        try: +            ok, req = signup.register_user(username, password) +        except socket.timeout: +            self.set_status_timeout() +            return False + +        if ok: +            return True + +        # something went wrong. +        # not registered, let's catch what. +        # get timeout +        # ... +        if req.status_code == 500: +            self.set_status_server_500() +            return False + +        validation_msgs = json.loads(req.content) +        logger.debug('validation errors: %s' % validation_msgs) +        errors = validation_msgs.get('errors', None) +        if errors and errors.get('login', None): +            self.set_status_invalid_username() +        else: +            self.set_status_unknown_error() +        return False + + +class GlobalEIPSettings(QtGui.QWizardPage): +    def __init__(self, parent=None): +        super(GlobalEIPSettings, self).__init__(parent) + + +class LastPage(QtGui.QWizardPage): +    def __init__(self, parent=None): +        super(LastPage, self).__init__(parent) + +        self.setTitle("Ready to go!") + +        #self.setPixmap( +            #QtGui.QWizard.WatermarkPixmap, +            #QtGui.QPixmap(':/images/watermark2.png')) + +        self.label = QtGui.QLabel() +        self.label.setWordWrap(True) + +        layout = QtGui.QVBoxLayout() +        layout.addWidget(self.label) +        self.setLayout(layout) + +    def initializePage(self): +        finishText = self.wizard().buttonText( +            QtGui.QWizard.FinishButton) +        finishText = finishText.replace('&', '') +        self.label.setText( +            "Click '<i>%s</i>' to end the wizard and start " +            "encrypting your connection." % finishText) + + +if __name__ == '__main__': +    # standalone test +    import sys +    import logging +    logging.basicConfig() +    logger = logging.getLogger() +    logger.setLevel(logging.DEBUG) + +    app = QtGui.QApplication(sys.argv) +    wizard = FirstRunWizard() +    wizard.show() +    sys.exit(app.exec_()) diff --git a/src/leap/gui/tests/integration/fake_user_signup.py b/src/leap/gui/tests/integration/fake_user_signup.py new file mode 100644 index 00000000..12f18966 --- /dev/null +++ b/src/leap/gui/tests/integration/fake_user_signup.py @@ -0,0 +1,80 @@ +""" +simple server to test registration and +authentication + +To test: + +curl -d login=python_test_user -d password_salt=54321\ +     -d password_verifier=12341234 \ +        http://localhost:8000/users.json + +""" +from BaseHTTPServer import HTTPServer +from BaseHTTPServer import BaseHTTPRequestHandler +import cgi +import urlparse + +HOST = "localhost" +PORT = 8000 + +LOGIN_ERROR = """{"errors":{"login":["has already been taken"]}}""" + + +class request_handler(BaseHTTPRequestHandler): +    responses = { +        '/': ['ok\n'], +        '/users.json': ['ok\n'], +        '/timeout': ['ok\n'] +    } + +    def do_GET(self): +        path = urlparse.urlparse(self.path) +        message = '\n'.join( +            self.responses.get( +                path.path, None)) +        self.send_response(200) +        self.end_headers() +        self.wfile.write(message) + +    def do_POST(self): +        form = cgi.FieldStorage( +            fp=self.rfile, +            headers=self.headers, +            environ={'REQUEST_METHOD': 'POST', +                     'CONTENT_TYPE': self.headers['Content-Type'], +                     }) +        data = dict( +            (key, form[key].value) for key in form.keys()) +        path = urlparse.urlparse(self.path) +        message = '\n'.join( +            self.responses.get( +                path.path, '')) + +        login = data.get('login', None) +        #password_salt = data.get('password_salt', None) +        #password_verifier = data.get('password_verifier', None) + +        if path.geturl() == "/timeout": +            print 'timeout' +            self.send_response(200) +            self.end_headers() +            self.wfile.write(message) +            import time +            time.sleep(10) +            return + +        ok = True if (login == "python_test_user") else False +        if ok: +            self.send_response(200) +            self.end_headers() +            self.wfile.write(message) + +        else: +            self.send_response(500) +            self.end_headers() +            self.wfile.write(LOGIN_ERROR) + + +if __name__ == "__main__": +    server = HTTPServer((HOST, PORT), request_handler) +    server.serve_forever() | 
