summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/feature_move-srpregister-to-backend1
-rw-r--r--src/leap/bitmask/backend.py84
-rw-r--r--src/leap/bitmask/crypto/srpregister.py63
-rw-r--r--src/leap/bitmask/gui/wizard.py106
4 files changed, 191 insertions, 63 deletions
diff --git a/changes/feature_move-srpregister-to-backend b/changes/feature_move-srpregister-to-backend
new file mode 100644
index 00000000..0625432f
--- /dev/null
+++ b/changes/feature_move-srpregister-to-backend
@@ -0,0 +1 @@
+- Move/refactor srpregister to backend.
diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py
index 99e5a04b..45ea451c 100644
--- a/src/leap/bitmask/backend.py
+++ b/src/leap/bitmask/backend.py
@@ -19,6 +19,7 @@ Backend for everything
"""
import logging
+from functools import partial
from Queue import Queue, Empty
from twisted.internet import threads, defer
@@ -28,6 +29,7 @@ from twisted.python import log
import zope.interface
from leap.bitmask.config.providerconfig import ProviderConfig
+from leap.bitmask.crypto.srpregister import SRPRegister
from leap.bitmask.provider import get_provider_path
from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
@@ -134,6 +136,9 @@ class Provider(object):
:param provider: URL for the provider
:type provider: unicode
+
+ :returns: the defer for the operation running in a thread.
+ :rtype: twisted.internet.defer.Deferred
"""
log.msg("Setting up provider %s..." % (provider.encode("idna"),))
pb = self._provider_bootstrapper
@@ -155,8 +160,10 @@ class Provider(object):
:param provider: URL for the provider
:type provider: unicode
- """
+ :returns: the defer for the operation running in a thread.
+ :rtype: twisted.internet.defer.Deferred
+ """
d = None
# If there's no loaded provider or
@@ -180,6 +187,57 @@ class Provider(object):
return d
+class Register(object):
+ """
+ Interfaces with setup and bootstrapping operations for a provider
+ """
+
+ zope.interface.implements(ILEAPComponent)
+
+ def __init__(self, signaler=None):
+ """
+ Constructor for the Register component
+
+ :param signaler: Object in charge of handling communication
+ back to the frontend
+ :type signaler: Signaler
+ """
+ object.__init__(self)
+ self.key = "register"
+ self._signaler = signaler
+ self._provider_config = ProviderConfig()
+
+ def register_user(self, domain, username, password):
+ """
+ Register a user using the domain and password given as parameters.
+
+ :param domain: the domain we need to register the user.
+ :type domain: unicode
+ :param username: the user name
+ :type username: unicode
+ :param password: the password for the username
+ :type password: unicode
+
+ :returns: the defer for the operation running in a thread.
+ :rtype: twisted.internet.defer.Deferred
+ """
+ # If there's no loaded provider or
+ # we want to connect to other provider...
+ if (not self._provider_config.loaded() or
+ self._provider_config.get_domain() != domain):
+ self._provider_config.load(get_provider_path(domain))
+
+ if self._provider_config.loaded():
+ srpregister = SRPRegister(signaler=self._signaler,
+ provider_config=self._provider_config)
+ return threads.deferToThread(
+ partial(srpregister.register_user, username, password))
+ else:
+ if self._signaler is not None:
+ self._signaler.signal(self._signaler.srp_registration_failed)
+ logger.error("Could not load provider configuration.")
+
+
class Signaler(QtCore.QObject):
"""
Signaler object, handles converting string commands to Qt signals.
@@ -188,8 +246,9 @@ class Signaler(QtCore.QObject):
live in the frontend.
"""
- # Signals for the ProviderBootstrapper
+ ####################
# These will only exist in the frontend
+ # Signals for the ProviderBootstrapper
prov_name_resolution = QtCore.Signal(object)
prov_https_connection = QtCore.Signal(object)
prov_download_provider_info = QtCore.Signal(object)
@@ -205,7 +264,13 @@ class Signaler(QtCore.QObject):
prov_cancelled_setup = QtCore.Signal(object)
- # These will exist both in the backend and the front end.
+ # Signals for SRPRegister
+ srp_registration_finished = QtCore.Signal(object)
+ srp_registration_failed = QtCore.Signal(object)
+ srp_registration_taken = QtCore.Signal(object)
+
+ ####################
+ # These will exist both in the backend AND the front end.
# The frontend might choose to not "interpret" all the signals
# from the backend, but the backend needs to have all the signals
# it's going to emit defined here
@@ -220,6 +285,10 @@ class Signaler(QtCore.QObject):
PROV_UNSUPPORTED_API = "prov_unsupported_api"
PROV_CANCELLED_SETUP = "prov_cancelled_setup"
+ SRP_REGISTRATION_FINISHED = "srp_registration_finished"
+ SRP_REGISTRATION_FAILED = "srp_registration_failed"
+ SRP_REGISTRATION_TAKEN = "srp_registration_taken"
+
def __init__(self):
"""
Constructor for the Signaler
@@ -238,6 +307,10 @@ class Signaler(QtCore.QObject):
self.PROV_UNSUPPORTED_CLIENT,
self.PROV_UNSUPPORTED_API,
self.PROV_CANCELLED_SETUP,
+
+ self.SRP_REGISTRATION_FINISHED,
+ self.SRP_REGISTRATION_FAILED,
+ self.SRP_REGISTRATION_TAKEN,
]
for sig in signals:
@@ -296,6 +369,7 @@ class Backend(object):
# Component registration
self._register(Provider(self._signaler, bypass_checks))
+ self._register(Register(self._signaler))
# We have a looping call on a thread executing all the
# commands in queue. Right now this queue is an actual Queue
@@ -409,3 +483,7 @@ class Backend(object):
def provider_bootstrap(self, provider):
self._call_queue.put(("provider", "bootstrap", None, provider))
+
+ def register_user(self, provider, username, password):
+ self._call_queue.put(("register", "register_user", None, provider,
+ username, password))
diff --git a/src/leap/bitmask/crypto/srpregister.py b/src/leap/bitmask/crypto/srpregister.py
index 02a1ea63..4c52db42 100644
--- a/src/leap/bitmask/crypto/srpregister.py
+++ b/src/leap/bitmask/crypto/srpregister.py
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import binascii
+import json
import logging
import requests
@@ -26,6 +27,7 @@ from urlparse import urlparse
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.util.constants import SIGNUP_TIMEOUT
+from leap.bitmask.util.request_helpers import get_content
from leap.common.check import leap_assert, leap_assert_type
logger = logging.getLogger(__name__)
@@ -40,16 +42,22 @@ class SRPRegister(QtCore.QObject):
USER_VERIFIER_KEY = 'user[password_verifier]'
USER_SALT_KEY = 'user[password_salt]'
+ STATUS_OK = (200, 201)
+ STATUS_TAKEN = 422
+ STATUS_ERROR = -999 # Custom error status
+
registration_finished = QtCore.Signal(bool, object)
- def __init__(self,
- provider_config=None,
- register_path="users"):
+ def __init__(self, signaler=None,
+ provider_config=None, register_path="users"):
"""
Constructor
+ :param signaler: Signaler object used to receive notifications
+ from the backend
+ :type signaler: Signaler
:param provider_config: provider configuration instance,
- properly loaded
+ properly loaded
:type privider_config: ProviderConfig
:param register_path: webapp path for registering users
:type register_path; str
@@ -59,6 +67,7 @@ class SRPRegister(QtCore.QObject):
leap_assert_type(provider_config, ProviderConfig)
self._provider_config = provider_config
+ self._signaler = signaler
# **************************************************** #
# Dependency injection helpers, override this for more
@@ -104,8 +113,8 @@ class SRPRegister(QtCore.QObject):
:param password: password for this username
:type password: str
- :rtype: tuple
- :rparam: (ok, request)
+ :returns: if the registration went ok or not.
+ :rtype: bool
"""
username = username.lower().encode('utf-8')
@@ -129,11 +138,7 @@ class SRPRegister(QtCore.QObject):
logger.debug("Will try to register user = %s" % (username,))
ok = False
- # This should be None, but we don't like when PySide segfaults,
- # so it something else.
- # To reproduce it, just do:
- # self.registration_finished.emit(False, None)
- req = []
+ req = None
try:
req = self._session.post(uri,
data=user_data,
@@ -143,13 +148,45 @@ class SRPRegister(QtCore.QObject):
except requests.exceptions.RequestException as exc:
logger.error(exc.message)
- ok = False
else:
ok = req.ok
- self.registration_finished.emit(ok, req)
+ status_code = self.STATUS_ERROR
+ if req is not None:
+ status_code = req.status_code
+ self._emit_result(status_code)
+
+ if not ok:
+ 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" % (username, error_msg)
+ logger.error(error_msg)
+ except Exception as e:
+ logger.error("Unknown error: %r" % (e, ))
+
return ok
+ def _emit_result(self, status_code):
+ """
+ Emit the corresponding signal depending on the status code.
+
+ :param status_code: the status code received.
+ :type status_code: int or str
+ """
+ logger.debug("Status code is: {0}".format(status_code))
+ if self._signaler is None:
+ return
+
+ if status_code in self.STATUS_OK:
+ self._signaler.signal(self._signaler.SRP_REGISTRATION_FINISHED)
+ elif status_code == self.STATUS_TAKEN:
+ self._signaler.signal(self._signaler.SRP_REGISTRATION_TAKEN)
+ else:
+ self._signaler.signal(self._signaler.SRP_REGISTRATION_FAILED)
+
if __name__ == "__main__":
logger = logging.getLogger(name='leap')
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index 024b23bc..2f274e33 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -18,22 +18,18 @@
First run wizard
"""
import logging
-import json
import random
from functools import partial
from PySide import QtCore, QtGui
-from twisted.internet import threads
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.crypto.srpregister import SRPRegister
from leap.bitmask.provider import get_provider_path
from leap.bitmask.services import get_service_display_name, get_supported
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.password import basic_password_checks
-from leap.bitmask.util.request_helpers import get_content
from ui_wizard import Ui_Wizard
@@ -253,16 +249,11 @@ class Wizard(QtGui.QWizard):
ok, msg = basic_password_checks(username, password, password2)
if ok:
- register = SRPRegister(provider_config=self._provider_config)
- register.registration_finished.connect(
- self._registration_finished)
-
- threads.deferToThread(
- partial(register.register_user, username, password))
+ self._set_register_status(self.tr("Starting registration..."))
+ self._backend.register_user(self._domain, username, password)
self._username = username
self._password = password
- self._set_register_status(self.tr("Starting registration..."))
else:
self._set_register_status(msg, error=True)
self._focus_password()
@@ -289,42 +280,59 @@ class Wizard(QtGui.QWizard):
# 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("Something has gone wrong. "
- "Please try again.")
- 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 _registration_finished(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_finished
+
+ The registration has finished successfully, so we do some final steps.
+ """
+ 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)
+
+ def _registration_failed(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_failed
+
+ The registration has failed, so we report the problem.
+ """
+ self._username = self._password = None
+
+ error_msg = self.tr("Something has gone wrong. Please try again.")
+ self._set_register_status(error_msg, error=True)
+ self.ui.btnRegister.setEnabled(True)
+
+ def _registration_taken(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_taken
+
+ The requested username is taken, warn the user about that.
+ """
+ self._username = self._password = None
+
+ error_msg = self.tr("The requested username is taken, choose another.")
+ self._set_register_status(error_msg, error=True)
+ self.ui.btnRegister.setEnabled(True)
def _set_register_status(self, status, error=False):
"""
@@ -688,6 +696,10 @@ class Wizard(QtGui.QWizard):
sig.prov_check_ca_fingerprint.connect(self._check_ca_fingerprint)
sig.prov_check_api_certificate.connect(self._check_api_certificate)
+ sig.srp_registration_finished.connect(self._registration_finished)
+ sig.srp_registration_failed.connect(self._registration_failed)
+ sig.srp_registration_taken.connect(self._registration_taken)
+
def _backend_disconnect(self):
"""
This method is called when the wizard dialog is closed.