#!/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.

" "If you ever need to modify these options again, " "you can find the wizard in the 'Settings' 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 '%s' 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_())