summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/leap/gui/firstrun/login.py247
-rw-r--r--src/leap/gui/firstrun/register.py7
-rwxr-xr-xsrc/leap/gui/firstrun/tests/integration/fake_provider.py31
-rw-r--r--src/leap/gui/tests/test_firstrun_login.py212
-rw-r--r--src/leap/gui/tests/test_firstrun_register.py20
5 files changed, 377 insertions, 140 deletions
diff --git a/src/leap/gui/firstrun/login.py b/src/leap/gui/firstrun/login.py
index 02bace86..e7afee9f 100644
--- a/src/leap/gui/firstrun/login.py
+++ b/src/leap/gui/firstrun/login.py
@@ -82,6 +82,120 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
#self.registerField('is_login_wizard')
+ # actual checks
+
+ def _do_checks(self):
+
+ full_username = self.userNameLineEdit.text()
+ ###########################
+ # 0) check user@domain form
+ ###########################
+
+ def checkusername():
+ if full_username.count('@') != 1:
+ return self.fail(
+ self.tr(
+ "Username must be in the username@provider form."))
+ else:
+ return True
+
+ yield(("head_sentinel", 0), checkusername)
+
+ username, domain = full_username.split('@')
+ password = self.userPasswordLineEdit.text()
+
+ # We try a call to an authenticated
+ # page here as a mean to catch
+ # srp authentication errors while
+ wizard = self.wizard()
+ eipconfigchecker = wizard.eipconfigchecker()
+
+ ########################
+ # 1) try name resolution
+ ########################
+ # show the frame before going on...
+ QtCore.QMetaObject.invokeMethod(
+ self, "showStepsFrame")
+
+ # Able to contact domain?
+ # can get definition?
+ # two-by-one
+ def resolvedomain():
+ try:
+ eipconfigchecker.fetch_definition(domain=domain)
+
+ # we're using requests here for all
+ # the possible error cases that it catches.
+ except requests.exceptions.ConnectionError as exc:
+ return self.fail(exc.message[1])
+ except requests.exceptions.HTTPError as exc:
+ return self.fail(exc.message)
+ except Exception as exc:
+ # XXX get catchall error msg
+ return self.fail(
+ exc.message)
+ else:
+ return True
+
+ yield((self.tr("Resolving domain name"), 20), resolvedomain)
+
+ wizard.set_providerconfig(
+ eipconfigchecker.defaultprovider.config)
+
+ ########################
+ # 2) do authentication
+ ########################
+ credentials = username, password
+ pCertChecker = wizard.providercertchecker(
+ domain=domain)
+
+ def validate_credentials():
+ #################
+ # FIXME #BUG #638
+ verify = False
+
+ try:
+ pCertChecker.download_new_client_cert(
+ credentials=credentials,
+ verify=verify)
+
+ except auth.SRPAuthenticationError as exc:
+ return self.fail(
+ self.tr("Authentication error: %s" % exc.message))
+
+ except Exception as exc:
+ return self.fail(exc.message)
+
+ else:
+ return True
+
+ yield(('Validating credentials', 60), validate_credentials)
+
+ self.set_done()
+ yield(("end_sentinel", 100), lambda: None)
+
+ def green_validation_status(self):
+ val = self.validationMsg
+ val.setText(self.tr('Credentials validated.'))
+ val.setStyleSheet(styles.GreenLineEdit)
+
+ def on_checks_validation_ready(self):
+ """
+ after checks
+ """
+ if self.is_done():
+ self.disableFields()
+ self.cleanup_errormsg()
+ self.clean_wizard_errors(self.current_page)
+ # make the user confirm the transition
+ # to next page.
+ self.nextText('&Next')
+ self.nextFocus()
+ self.green_validation_status()
+ self.do_confirm_next = True
+
+ # ui update
+
def nextText(self, text):
self.setButtonText(
QtGui.QWizard.NextButton, text)
@@ -94,12 +208,18 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
self.wizard().button(
QtGui.QWizard.NextButton).setDisabled(True)
- def onUserNameEdit(self, *args):
+ def onUserNamePositionChanged(self, *args):
if self.initial_username_sample:
self.userNameLineEdit.setText('')
# XXX set regular color
self.initial_username_sample = None
+ def onUserNameTextChanged(self, *args):
+ if self.initial_username_sample:
+ k = args[0][-1]
+ self.initial_username_sample = None
+ self.userNameLineEdit.setText(k)
+
def disableFields(self):
for field in (self.userNameLineEdit,
self.userPasswordLineEdit):
@@ -111,13 +231,8 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
errors = self.wizard().get_validation_error(
self.current_page)
- #prev_er = getattr(self, 'prevalidation_error', None)
showerr = self.validationMsg.setText
- #if not errors and prev_er:
- #showerr(prev_er)
- #return
-#
if errors:
bad_str = getattr(self, 'bad_string', None)
cur_str = self.userNameLineEdit.text()
@@ -128,9 +243,6 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
self.bad_string = cur_str
showerr(errors)
else:
- #if prev_er:
- #showerr(prev_er)
- #return
# not the first time
if cur_str == bad_str:
showerr(errors)
@@ -177,7 +289,9 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
username = self.userNameLineEdit
username.setText('username@provider.example.org')
username.cursorPositionChanged.connect(
- self.onUserNameEdit)
+ self.onUserNamePositionChanged)
+ username.textChanged.connect(
+ self.onUserNameTextChanged)
self.initial_username_sample = True
self.validationMsg.setText('')
self.valFrame.hide()
@@ -215,116 +329,3 @@ class LogInPage(InlineValidationPage, UserFormMixIn): # InlineValidationPage
self.do_checks()
return self.is_done()
-
- def _do_checks(self):
- # XXX convert this to inline
-
- full_username = self.userNameLineEdit.text()
- ###########################
- # 0) check user@domain form
- ###########################
-
- def checkusername():
- if full_username.count('@') != 1:
- return self.fail(
- self.tr(
- "Username must be in the username@provider form."))
- else:
- return True
-
- yield(("head_sentinel", 0), checkusername)
-
- # XXX I think this is not needed
- # since we're also checking for the is_signup field.
- #self.wizard().from_login = True
-
- username, domain = full_username.split('@')
- password = self.userPasswordLineEdit.text()
-
- # We try a call to an authenticated
- # page here as a mean to catch
- # srp authentication errors while
- wizard = self.wizard()
- eipconfigchecker = wizard.eipconfigchecker()
-
- ########################
- # 1) try name resolution
- ########################
- # show the frame before going on...
- QtCore.QMetaObject.invokeMethod(
- self, "showStepsFrame")
-
- # Able to contact domain?
- # can get definition?
- # two-by-one
- def resolvedomain():
- try:
- eipconfigchecker.fetch_definition(domain=domain)
-
- # we're using requests here for all
- # the possible error cases that it catches.
- except requests.exceptions.ConnectionError as exc:
- return self.fail(exc.message[1])
- except requests.exceptions.HTTPError as exc:
- return self.fail(exc.message)
- except Exception as exc:
- # XXX get catchall error msg
- return self.fail(
- exc.message)
-
- yield((self.tr("resolving domain name"), 20), resolvedomain)
-
- wizard.set_providerconfig(
- eipconfigchecker.defaultprovider.config)
-
- ########################
- # 2) do authentication
- ########################
- credentials = username, password
- pCertChecker = wizard.providercertchecker(
- domain=domain)
-
- def validate_credentials():
- #################
- # FIXME #BUG #638
- verify = False
-
- try:
- pCertChecker.download_new_client_cert(
- credentials=credentials,
- verify=verify)
-
- except auth.SRPAuthenticationError as exc:
- return self.fail(
- self.tr("Authentication error: %s" % exc.message))
-
- except Exception as exc:
- return self.fail(exc.message)
-
- else:
- return True
-
- yield(('Validating credentials', 20), validate_credentials)
-
- self.set_done()
- yield(("end_sentinel", 0), lambda: None)
-
- def green_validation_status(self):
- val = self.validationMsg
- val.setText(self.tr('Credentials validated.'))
- val.setStyleSheet(styles.GreenLineEdit)
-
- def on_checks_validation_ready(self):
- """
- after checks
- """
- if self.is_done():
- self.disableFields()
- self.cleanup_errormsg()
- self.clean_wizard_errors(self.current_page)
- # make the user confirm the transition
- # to next page.
- self.nextText('&Next')
- self.nextFocus()
- self.green_validation_status()
- self.do_confirm_next = True
diff --git a/src/leap/gui/firstrun/register.py b/src/leap/gui/firstrun/register.py
index 7fd5c574..4c811093 100644
--- a/src/leap/gui/firstrun/register.py
+++ b/src/leap/gui/firstrun/register.py
@@ -363,9 +363,6 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
inits wizard page
"""
provider = unicode(self.field('provider_domain'))
- # hack. don't get why I'm getting a QVariant there,
- # making segfault in tests.
- provider = QtCore.QString(provider)
if provider:
# here we should have provider
# but in tests we might not.
@@ -384,7 +381,7 @@ class RegisterUserPage(InlineValidationPage, UserFormMixIn):
def nextId(self):
wizard = self.wizard()
- if not wizard:
- return
+ #if not wizard:
+ #return
# XXX this should be called connect
return wizard.get_page_index('signupvalidation')
diff --git a/src/leap/gui/firstrun/tests/integration/fake_provider.py b/src/leap/gui/firstrun/tests/integration/fake_provider.py
index 33ee0ee6..445b4487 100755
--- a/src/leap/gui/firstrun/tests/integration/fake_provider.py
+++ b/src/leap/gui/firstrun/tests/integration/fake_provider.py
@@ -40,6 +40,8 @@ from twisted.web.static import File
from twisted.web.resource import Resource
from twisted.internet import reactor
+from leap.testing.https_server import where
+
# See
# http://twistedmatrix.com/documents/current/web/howto/web-in-60/index.htmln
# for more examples
@@ -229,14 +231,13 @@ def get_certs_path():
def get_TLS_credentials():
# XXX this is giving errors
# XXX REview! We want to use gnutls!
- certs_path = get_certs_path()
cert = crypto.X509Certificate(
- open(certs_path + '/leaptestscert.pem').read())
+ open(where('leaptestscert.pem')).read())
key = crypto.X509PrivateKey(
- open(certs_path + '/leaptestskey.pem').read())
+ open(where('leaptestskey.pem')).read())
ca = crypto.X509Certificate(
- open(certs_path + '/cacert.pem').read())
+ open(where('cacert.pem')).read())
#crl = crypto.X509CRL(open(certs_path + '/crl.pem').read())
#cred = crypto.X509Credentials(cert, key, [ca], [crl])
cred = X509Credentials(cert, key, [ca])
@@ -253,19 +254,17 @@ class OpenSSLServerContextFactory:
"""Create an SSL context.
This is a sample implementation that loads a certificate from a file
called 'server.pem'."""
- certs_path = get_certs_path()
ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.use_certificate_file(certs_path + '/leaptestscert.pem')
- ctx.use_privatekey_file(certs_path + '/leaptestskey.pem')
+ #certs_path = get_certs_path()
+ #ctx.use_certificate_file(certs_path + '/leaptestscert.pem')
+ #ctx.use_privatekey_file(certs_path + '/leaptestskey.pem')
+ ctx.use_certificate_file(where('leaptestscert.pem'))
+ ctx.use_privatekey_file(where('leaptestskey.pem'))
return ctx
-if __name__ == "__main__":
-
- from twisted.python import log
- log.startLogging(sys.stdout)
-
+def serve_fake_provider():
root = Resource()
root.putChild("provider.json", File("./provider.json"))
config = Resource()
@@ -293,3 +292,11 @@ if __name__ == "__main__":
reactor.listenSSL(8443, factory, OpenSSLServerContextFactory())
reactor.run()
+
+
+if __name__ == "__main__":
+
+ from twisted.python import log
+ log.startLogging(sys.stdout)
+
+ serve_fake_provider()
diff --git a/src/leap/gui/tests/test_firstrun_login.py b/src/leap/gui/tests/test_firstrun_login.py
new file mode 100644
index 00000000..fa800c23
--- /dev/null
+++ b/src/leap/gui/tests/test_firstrun_login.py
@@ -0,0 +1,212 @@
+import sys
+import unittest
+
+import mock
+
+from leap.testing import qunittest
+#from leap.testing import pyqt
+
+from PyQt4 import QtGui
+#from PyQt4 import QtCore
+#import PyQt4.QtCore # some weirdness with mock module
+
+from PyQt4.QtTest import QTest
+from PyQt4.QtCore import Qt
+
+from leap.gui import firstrun
+
+try:
+ from collections import OrderedDict
+except ImportError:
+ # We must be in 2.6
+ from leap.util.dicts import OrderedDict
+
+
+class TestPage(firstrun.login.LogInPage):
+ pass
+
+
+class LogInPageLogicTestCase(qunittest.TestCase):
+
+ # XXX can spy on signal connections
+ __name__ = "register user page logic tests"
+
+ def setUp(self):
+ self.app = QtGui.QApplication(sys.argv)
+ QtGui.qApp = self.app
+ self.page = TestPage(None)
+ self.page.wizard = mock.MagicMock()
+
+ def tearDown(self):
+ QtGui.qApp = None
+ self.app = None
+ self.page = None
+
+ def test__do_checks(self):
+ eq = self.assertEqual
+
+ self.page.userNameLineEdit.setText('testuser@domain')
+ self.page.userPasswordLineEdit.setText('testpassword')
+
+ # fake register process
+ with mock.patch('leap.base.auth.LeapSRPRegister') as mockAuth:
+ mockSignup = mock.MagicMock()
+
+ reqMockup = mock.Mock()
+ # XXX should inject bad json to get error
+ reqMockup.content = '{"errors": null}'
+ mockSignup.register_user.return_value = (True, reqMockup)
+ mockAuth.return_value = mockSignup
+ checks = [x for x in self.page._do_checks()]
+
+ eq(len(checks), 4)
+ labels = [str(x) for (x, y), z in checks]
+ eq(labels, ['head_sentinel',
+ 'Resolving domain name',
+ 'Validating credentials',
+ 'end_sentinel'])
+ progress = [y for (x, y), z in checks]
+ eq(progress, [0, 20, 60, 100])
+
+ # normal run, ie, no exceptions
+
+ checkfuns = [z for (x, y), z in checks]
+ checkusername, resolvedomain, valcreds = checkfuns[:-1]
+
+ self.assertTrue(checkusername())
+ #self.mocknetchecker.check_name_resolution.assert_called_with(
+ #'test_provider1')
+
+ self.assertTrue(resolvedomain())
+ #self.mockpcertchecker.is_https_working.assert_called_with(
+ #"https://test_provider1", verify=True)
+
+ self.assertTrue(valcreds())
+
+ # XXX missing: inject failing exceptions
+ # XXX TODO make it break
+
+
+class RegisterUserPageUITestCase(qunittest.TestCase):
+
+ # XXX can spy on signal connections
+ __name__ = "Register User Page UI tests"
+
+ def setUp(self):
+ self.app = QtGui.QApplication(sys.argv)
+ QtGui.qApp = self.app
+
+ self.pagename = "signup"
+ pages = OrderedDict((
+ (self.pagename, TestPage),
+ ('providersetupvalidation',
+ firstrun.regvalidation.RegisterUserValidationPage)))
+ self.wizard = firstrun.wizard.FirstRunWizard(None, pages_dict=pages)
+ self.page = self.wizard.page(self.wizard.get_page_index(self.pagename))
+
+ self.page.do_checks = mock.Mock()
+
+ # wizard would do this for us
+ self.page.initializePage()
+
+ def tearDown(self):
+ QtGui.qApp = None
+ self.app = None
+ self.wizard = None
+
+ # XXX refactor out
+ def fill_field(self, field, text):
+ """
+ fills a field (line edit) that is passed along
+ :param field: the qLineEdit
+ :param text: the text to be filled
+ :type field: QLineEdit widget
+ :type text: str
+ """
+ keyp = QTest.keyPress
+ field.setFocus(True)
+ for c in text:
+ keyp(field, c)
+ self.assertEqual(field.text(), text)
+
+ def del_field(self, field):
+ """
+ deletes entried text in
+ field line edit
+ :param field: the QLineEdit
+ :type field: QLineEdit widget
+ """
+ keyp = QTest.keyPress
+ for c in range(len(field.text())):
+ keyp(field, Qt.Key_Backspace)
+ self.assertEqual(field.text(), "")
+
+ def test_buttons_disabled_until_textentry(self):
+ # it's a commit button this time
+ nextbutton = self.wizard.button(QtGui.QWizard.CommitButton)
+
+ self.assertFalse(nextbutton.isEnabled())
+
+ f_username = self.page.userNameLineEdit
+ f_password = self.page.userPasswordLineEdit
+
+ self.fill_field(f_username, "testuser")
+ self.fill_field(f_password, "testpassword")
+
+ # commit should be enabled
+ # XXX Need a workaround here
+ # because the isComplete is not being evaluated...
+ # (no event loop running??)
+ #import ipdb;ipdb.set_trace()
+ #self.assertTrue(nextbutton.isEnabled())
+ self.assertTrue(self.page.isComplete())
+
+ self.del_field(f_username)
+ self.del_field(f_password)
+
+ # after rm fields commit button
+ # should be disabled again
+ #self.assertFalse(nextbutton.isEnabled())
+ self.assertFalse(self.page.isComplete())
+
+ def test_validate_page(self):
+ self.assertFalse(self.page.validatePage())
+ # XXX TODO MOAR CASES...
+ # add errors, False
+ # change done, False
+ # not done, do_checks called
+ # click confirm, True
+ # done and do_confirm, True
+
+ def test_next_id(self):
+ self.assertEqual(self.page.nextId(), 1)
+
+ def test_paint_event(self):
+ self.page.populateErrors = mock.Mock()
+ self.page.paintEvent(None)
+ self.page.populateErrors.assert_called_with()
+
+ def test_validation_ready(self):
+ f_username = self.page.userNameLineEdit
+ f_password = self.page.userPasswordLineEdit
+
+ self.fill_field(f_username, "testuser")
+ self.fill_field(f_password, "testpassword")
+
+ self.page.done = True
+ self.page.on_checks_validation_ready()
+ self.assertFalse(f_username.isEnabled())
+ self.assertFalse(f_password.isEnabled())
+
+ self.assertEqual(self.page.validationMsg.text(),
+ "Credentials validated.")
+ self.assertEqual(self.page.do_confirm_next, True)
+
+ def test_regex(self):
+ # XXX enter invalid username with key presses
+ # check text is not updated
+ pass
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/leap/gui/tests/test_firstrun_register.py b/src/leap/gui/tests/test_firstrun_register.py
index be38e87c..3447fe9d 100644
--- a/src/leap/gui/tests/test_firstrun_register.py
+++ b/src/leap/gui/tests/test_firstrun_register.py
@@ -220,5 +220,25 @@ class RegisterUserPageUITestCase(qunittest.TestCase):
self.page.paintEvent(None)
self.page.populateErrors.assert_called_with()
+ def test_validation_ready(self):
+ f_username = self.page.userNameLineEdit
+ f_password = self.page.userPasswordLineEdit
+ f_passwor2 = self.page.userPassword2LineEdit
+
+ self.fill_field(f_username, "testuser")
+ self.fill_field(f_password, "testpassword")
+ self.fill_field(f_passwor2, "testpassword")
+
+ self.page.done = True
+ self.page.on_checks_validation_ready()
+ self.assertFalse(f_username.isEnabled())
+ self.assertFalse(f_password.isEnabled())
+ self.assertFalse(f_passwor2.isEnabled())
+
+ self.assertEqual(self.page.validationMsg.text(),
+ "Registration succeeded!")
+ self.assertEqual(self.page.do_confirm_next, True)
+
+
if __name__ == "__main__":
unittest.main()