Exemple #1
0
 def __init__(self, bus_name, object_path=None,
              sso_login_processor_class=Account,
              sso_service_class=None):
     """Initiate the Login object."""
     super(SSOLogin, self).__init__()
     # ignore bus_name and object path so that we do not break the current
     # API. Shall we change this???
     self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class)
Exemple #2
0
 def __init__(self, bus_name, object_path=DBUS_ACCOUNT_PATH,
              sso_login_processor_class=Account,
              sso_service_class=None):
     """Initiate the Login object."""
     dbus.service.Object.__init__(self, object_path=object_path,
                                  bus_name=bus_name)
     self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class)
     msg = 'Use ubuntu_sso.main.CredentialsManagement instead.'
     warnings.warn(msg, DeprecationWarning)
 def _do_login(self, email, password):
     """Login using email/password, connect outcome signals."""
     from ubuntu_sso.main import SSOLoginRoot
     self.inner = SSOLoginRoot()
     self.inner.login(app_name=self.app_name, email=email,
                      password=password,
                      result_cb=self._login_success_cb,
                      error_cb=self._error_cb,
                      not_validated_cb=self._error_cb)
Exemple #4
0
class SSOLogin(Referenceable, SignalBroadcaster):
    """Login thru the Single Sign On service."""

    __metaclass__ = RemoteMeta

    # calls that will be accessible remotely
    remote_calls = [
        'generate_captcha',
        'register_user',
        'login',
        'validate_email',
        'request_password_reset_token',
        'set_new_password']

    def __init__(self, bus_name, object_path=None,
                 sso_login_processor_class=Account,
                 sso_service_class=None):
        """Initiate the Login object."""
        super(SSOLogin, self).__init__()
        # ignore bus_name and object path so that we do not break the current
        # API. Shall we change this???
        self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class)

    # generate_capcha signals
    def emit_captcha_generated(self, app_name, result):
        """Signal thrown after the captcha is generated."""
        logger.debug('SSOLogin: emitting CaptchaGenerated with app_name "%s" '
                     'and result %r', app_name, result)
        self.emit_signal('on_captcha_generated', app_name, result)

    def emit_captcha_generation_error(self, app_name, raised_error):
        """Signal thrown when there's a problem generating the captcha."""
        logger.debug('SSOLogin: emitting CaptchaGenerationError with '
                     'app_name "%s" and error %r', app_name, raised_error)
        self.emit_signal('on_captcha_generation_error', app_name,
                         except_to_errdict(raised_error.value))

    def generate_captcha(self, app_name, filename):
        """Call the matching method in the processor."""
        self.root.generate_captcha(app_name, filename,
                                   self.emit_captcha_generated,
                                   self.emit_captcha_generation_error)

    # register_user signals
    def emit_user_registered(self, app_name, result):
        """Signal thrown when the user is registered."""
        logger.debug('SSOLogin: emitting UserRegistered with app_name "%s" '
                     'and result %r', app_name, result)
        self.emit_signal('on_user_registered', app_name, result)

    def emit_user_registration_error(self, app_name, raised_error):
        """Signal thrown when there's a problem registering the user."""
        logger.debug('SSOLogin: emitting UserRegistrationError with '
                     'app_name "%s" and error %r', app_name, raised_error)
        self.emit_signal('on_user_registration_error', app_name,
                         except_to_errdict(raised_error.value))

    def register_user(self, app_name, email, password, displayname,
                      captcha_id, captcha_solution):
        """Call the matching method in the processor."""
        self.root.register_user(app_name, email, password, displayname,
                                captcha_id, captcha_solution,
                                self.emit_user_registered,
                                self.emit_user_registration_error)

    # login signals
    def emit_logged_in(self, app_name, result):
        """Signal thrown when the user is logged in."""
        logger.debug('SSOLogin: emitting LoggedIn with app_name "%s" '
                     'and result %r', app_name, result)
        self.emit_signal('on_logged_in', app_name, result)

    def emit_login_error(self, app_name, raised_error):
        """Signal thrown when there is a problem in the login."""
        logger.debug('SSOLogin: emitting LoginError with '
                     'app_name "%s" and error %r', app_name, raised_error)
        self.emit_signal('on_login_error', app_name,
                         except_to_errdict(raised_error.value))

    def emit_user_not_validated(self, app_name, result):
        """Signal thrown when the user is not validated."""
        logger.debug('SSOLogin: emitting UserNotValidated with app_name "%s" '
                     'and result %r', app_name, result)
        self.emit_signal('on_user_not_validated', app_name, result)

    def login(self, app_name, email, password):
        """Call the matching method in the processor."""
        self.root.login(app_name, email, password,
                        self.emit_logged_in, self.emit_login_error,
                        self.emit_user_not_validated)

    # validate_email signals
    def emit_email_validated(self, app_name, result):
        """Signal thrown after the email is validated."""
        logger.debug('SSOLogin: emitting EmailValidated with app_name "%s" '
                     'and result %r', app_name, result)
        self.emit_signal('on_email_validated', app_name, result)

    def emit_email_validation_error(self, app_name, raised_error):
        """Signal thrown when there's a problem validating the email."""
        logger.debug('SSOLogin: emitting EmailValidationError with '
                     'app_name "%s" and error %r', app_name, raised_error)
        self.emit_signal('on_email_validation_error', app_name,
                         except_to_errdict(raised_error.value))

    def validate_email(self, app_name, email, password, email_token):
        """Call the matching method in the processor."""
        self.root.validate_email(app_name, email, password, email_token,
                                 self.emit_email_validated,
                                 self.emit_email_validation_error)

    # request_password_reset_token signals
    def emit_password_reset_token_sent(self, app_name, result):
        """Signal thrown when the token is successfully sent."""
        logger.debug('SSOLogin: emitting PasswordResetTokenSent with app_name '
                     '"%s" and result %r', app_name, result)
        self.emit_signal('on_password_reset_token_sent', app_name, result)

    def emit_password_reset_error(self, app_name, raised_error):
        """Signal thrown when there's a problem sending the token."""
        logger.debug('SSOLogin: emitting PasswordResetError with '
                     'app_name "%s" and error %r', app_name, raised_error)
        self.emit_signal('on_password_reset_error', app_name,
                         except_to_errdict(raised_error.value))

    def request_password_reset_token(self, app_name, email):
        """Call the matching method in the processor."""
        self.root.request_password_reset_token(app_name, email,
                                        self.emit_password_reset_token_sent,
                                        self.emit_password_reset_error)

    # set_new_password signals
    def emit_password_changed(self, app_name, result):
        """Signal thrown when the token is successfully sent."""
        logger.debug('SSOLogin: emitting PasswordChanged with app_name "%s" '
                     'and result %r', app_name, result)
        self.emit_signal('on_password_changed', app_name, result)

    def emit_password_change_error(self, app_name, raised_error):
        """Signal thrown when there's a problem sending the token."""
        logger.debug('SSOLogin: emitting PasswordChangeError with '
                     'app_name "%s" and error %r', app_name, raised_error)
        self.emit_signal('on_password_change_error', app_name,
                         except_to_errdict(raised_error.value))

    def set_new_password(self, app_name, email, token, new_password):
        """Call the matching method in the processor."""
        self.root.set_new_password(app_name, email, token, new_password,
                                   self.emit_password_changed,
                                   self.emit_password_change_error)
Exemple #5
0
class SSOLogin(dbus.service.Object):
    """Login thru the Single Sign On service."""

    # Operator not preceded by a space (fails with dbus decorators)
    # pylint: disable=C0322

    def __init__(self, bus_name, object_path=DBUS_ACCOUNT_PATH,
                 sso_login_processor_class=Account,
                 sso_service_class=None):
        """Initiate the Login object."""
        dbus.service.Object.__init__(self, object_path=object_path,
                                     bus_name=bus_name)
        self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class)
        msg = 'Use ubuntu_sso.main.CredentialsManagement instead.'
        warnings.warn(msg, DeprecationWarning)

    # generate_capcha signals
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
    def CaptchaGenerated(self, app_name, result):
        """Signal thrown after the captcha is generated."""
        logger.debug('SSOLogin: emitting CaptchaGenerated with app_name "%s" '
                     'and result %r', app_name, result)

    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
    def CaptchaGenerationError(self, app_name, error):
        """Signal thrown when there's a problem generating the captcha."""
        logger.debug('SSOLogin: emitting CaptchaGenerationError with '
                     'app_name "%s" and error %r', app_name, error)

    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
                         in_signature='ss')
    def generate_captcha(self, app_name, filename):
        """Call the matching method in the processor."""
        self.root.generate_captcha(app_name, filename,
                                   self.CaptchaGenerated,
                                   self.CaptchaGenerationError)

    # register_user signals
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
    def UserRegistered(self, app_name, result):
        """Signal thrown when the user is registered."""
        logger.debug('SSOLogin: emitting UserRegistered with app_name "%s" '
                     'and result %r', app_name, result)

    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
    def UserRegistrationError(self, app_name, error):
        """Signal thrown when there's a problem registering the user."""
        logger.debug('SSOLogin: emitting UserRegistrationError with '
                     'app_name "%s" and error %r', app_name, error)

    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
                         in_signature='ssssss')
    def register_user(self, app_name, email, password, name,
                      captcha_id, captcha_solution):
        """Call the matching method in the processor."""
        self.root.register_user(app_name, email, password, name, captcha_id,
                                captcha_solution,
                                self.UserRegistered,
                                self.UserRegistrationError)

    # login signals
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
    def LoggedIn(self, app_name, result):
        """Signal thrown when the user is logged in."""
        logger.debug('SSOLogin: emitting LoggedIn with app_name "%s" '
                     'and result %r', app_name, result)

    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
    def LoginError(self, app_name, error):
        """Signal thrown when there is a problem in the login."""
        logger.debug('SSOLogin: emitting LoginError with '
                     'app_name "%s" and error %r', app_name, error)

    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
    def UserNotValidated(self, app_name, result):
        """Signal thrown when the user is not validated."""
        logger.debug('SSOLogin: emitting UserNotValidated with app_name "%s" '
                     'and result %r', app_name, result)

    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
                         in_signature='sss')
    def login(self, app_name, email, password):
        """Call the matching method in the processor."""
        self.root.login(app_name, email, password, self.LoggedIn,
                        self.LoginError, self.UserNotValidated)

    # validate_email signals
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
    def EmailValidated(self, app_name, result):
        """Signal thrown after the email is validated."""
        logger.debug('SSOLogin: emitting EmailValidated with app_name "%s" '
                     'and result %r', app_name, result)

    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
    def EmailValidationError(self, app_name, error):
        """Signal thrown when there's a problem validating the email."""
        logger.debug('SSOLogin: emitting EmailValidationError with '
                     'app_name "%s" and error %r', app_name, error)

    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
                         in_signature='ssss')
    def validate_email(self, app_name, email, password, email_token):
        """Call the matching method in the processor."""
        self.root.validate_email(app_name, email, password, email_token,
                                 self.EmailValidated,
                                 self.EmailValidationError)

    # request_password_reset_token signals
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
    def PasswordResetTokenSent(self, app_name, result):
        """Signal thrown when the token is succesfully sent."""
        logger.debug('SSOLogin: emitting PasswordResetTokenSent with app_name '
                     '"%s" and result %r', app_name, result)

    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
    def PasswordResetError(self, app_name, error):
        """Signal thrown when there's a problem sending the token."""
        logger.debug('SSOLogin: emitting PasswordResetError with '
                     'app_name "%s" and error %r', app_name, error)

    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
                         in_signature='ss')
    def request_password_reset_token(self, app_name, email):
        """Call the matching method in the processor."""
        self.root.request_password_reset_token(app_name, email,
                                               self.PasswordResetTokenSent,
                                               self.PasswordResetError)

    # set_new_password signals
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
    def PasswordChanged(self, app_name, result):
        """Signal thrown when the token is succesfully sent."""
        logger.debug('SSOLogin: emitting PasswordChanged with app_name "%s" '
                     'and result %r', app_name, result)

    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
    def PasswordChangeError(self, app_name, error):
        """Signal thrown when there's a problem sending the token."""
        logger.debug('SSOLogin: emitting PasswordChangeError with '
                     'app_name "%s" and error %r', app_name, error)

    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
                         in_signature='ssss')
    def set_new_password(self, app_name, email, token, new_password):
        """Call the matching method in the processor."""
        self.root.set_new_password(app_name, email, token, new_password,
                                   self.PasswordChanged,
                                   self.PasswordChangeError)
class Credentials(object):
    """Credentials management gateway."""

    def __init__(self, app_name, tc_url=None, help_text='',
                 window_id=0, ping_url=None,
                 ui_module='ubuntu_sso.gtk.gui', ui_class='UbuntuSSOClientGUI',
                 success_cb=NO_OP, error_cb=NO_OP, denial_cb=NO_OP):
        """Return a Credentials management object.

        'app_name' is the application name to be displayed in the GUI.

        'tc_url' is the URL pointing to Terms & Conditions. If None, no
        TOS agreement will be displayed.

        'help_text' is an explanatory text for the end-users, will be shown
         below the headers.

        'window_id' is the id of the window which will be set as a parent of
         the GUI. If 0, no parent will be set.

        'ping_url' is the url that will be pinged when a user registers/logins
        successfully. The user email will be attached to 'ping_url'.

        'success_cb' will be called when the credentials were retrieved
        successfully. Two params will be passed: the app_name and the
        credentials per se. The credentials is a dictionary of the form:

            {'token': <value>,
             'token_secret': <value>,
             'consumer_key': <value>,
             'consumer_secret': <value>,
             'name': <the token name, matches "[app_name] @ [host name]">}

        'error_cb' will be called when the credentials retrieval failed. Two
        params will be passed: the app_name, and an error dict with 2 keys:
        the error message (user friendly, not translatable so far), and
        the detailed error (usually the traceback).

        'denial_cb' will be called when the user denied the credentials to the
        caller. A single param is passed: the app_name.

        """
        self.app_name = app_name
        self.help_text = help_text
        self.window_id = window_id
        self.ping_url = ping_url
        self.tc_url = tc_url
        self.ui_module = ui_module
        self.ui_class = ui_class
        self._success_cb = success_cb
        self._error_cb = error_cb
        self.denial_cb = denial_cb
        self.inner = None  # will hold the GUI or SSOLoginRoot instance

    @handle_failures(msg='Problem while retrieving credentials')
    @inlineCallbacks
    def _login_success_cb(self, app_name, email):
        """Store credentials when the login/registration succeeded.

        Also, open self.ping_url/email to notify about this new token. If any
        error occur, self.error_cb is called. Otherwise, self.success_cb is
        called.

        Return 0 on success, and a non-zero value (or None) on error.

        """
        logger.info('Login/registration was successful for app %r, email %r',
                    app_name, email)
        creds = yield self.find_credentials()
        if creds is not None:
            assert len(creds) > 0, 'Creds are empty! This should not happen'
            # ping a server with the credentials if we were requested to
            if self.ping_url is not None:
                status = yield self._ping_url(app_name, email, creds)
                if status is None:
                    yield self.clear_credentials()
                    return

            self.success_cb(creds)
            returnValue(0)

    def _auth_denial_cb(self, app_name):
        """The user decided not to allow the registration or login."""
        logger.warning('Login/registration was denied to app %r', app_name)
        self.denial_cb(app_name)

    @handle_failures(msg='Problem opening the ping_url')
    @inlineCallbacks
    def _ping_url(self, app_name, email, credentials):
        """Ping the self.ping_url with the email attached.

        Sign the request with the user credentials. The self.ping_url must be
        defined if this method is being called.

        """
        logger.info('Pinging server for app_name %r, ping_url: %r, '
                    'email %r.', app_name, self.ping_url, email)
        try:
            url = self.ping_url.format(email=email)
        except IndexError:  # tuple index out of range
            url = self.ping_url.format(email)  # format the first substitution

        if url == self.ping_url:
            logger.debug('Original url (%r) could not be formatted, '
                         'appending email (%r).', self.ping_url, email)
            url = self.ping_url + email

        headers = utils.oauth_headers(url, credentials)
        request = urllib2.Request(url, headers=headers)
        logger.debug('Opening the url %r with urllib2.urlopen.',
                     request.get_full_url())
        # This code is blocking, we should change this.
        # I've tried with deferToThread an twisted.web.client.getPage
        # but the returned deferred will never be fired (nataliabidart).
        response = urllib2.urlopen(request)
        logger.debug('Url opened. Response: %s.', response.code)
        returnValue(response)

    @handle_exceptions(msg='Problem opening the Ubuntu SSO user interface')
    def _show_ui(self, login_only):
        """Shows the UI, connect outcome signals."""

        __import__(self.ui_module)
        gui = sys.modules[self.ui_module]

        self.inner = getattr(gui, self.ui_class)(app_name=self.app_name,
                        tc_url=self.tc_url, help_text=self.help_text,
                        window_id=self.window_id, login_only=login_only)

        self.inner.login_success_callback = self._login_success_cb
        self.inner.registration_success_callback = self._login_success_cb
        self.inner.user_cancellation_callback = self._auth_denial_cb

    @handle_exceptions(msg='Problem logging with email and password.')
    def _do_login(self, email, password):
        """Login using email/password, connect outcome signals."""
        from ubuntu_sso.main import SSOLoginRoot
        self.inner = SSOLoginRoot()
        self.inner.login(app_name=self.app_name, email=email,
                         password=password,
                         result_cb=self._login_success_cb,
                         error_cb=self._error_cb,
                         not_validated_cb=self._error_cb)

    @handle_failures(msg='Problem while retrieving credentials')
    @inlineCallbacks
    def _login_or_register(self, login_only, email=None, password=None):
        """Get credentials if found else prompt the GUI."""
        logger.info("_login_or_register: login_only=%r email=%r.",
            login_only, email)
        token = yield self.find_credentials()
        if token is not None and len(token) > 0:
            self.success_cb(token)
        elif token == {}:
            if email and password:
                self._do_login(email, password)
            else:
                self._show_ui(login_only)
        else:
            # something went wrong with find_credentials, already handled.
            logger.info('_login_or_register: call to "find_credentials" went '
                        'wrong, and was already handled.')

    def error_cb(self, error_dict):
        """Handle error and notify the caller."""
        logger.error('Calling error callback at %r (error is %r).',
                     self._error_cb, error_dict)
        self._error_cb(self.app_name, error_dict)

    def success_cb(self, creds):
        """Handle success and notify the caller."""
        logger.debug('Calling success callback at %r.', self._success_cb)
        self._success_cb(self.app_name, creds)

    @inlineCallbacks
    def find_credentials(self):
        """Get the credentials for 'self.app_name'. Return {} if not there."""
        creds = yield Keyring().get_credentials(self.app_name)
        logger.info('find_credentials: self.app_name %r, '
                    'result is {}? %s', self.app_name, creds is None)
        returnValue(creds if creds is not None else {})

    @inlineCallbacks
    def clear_credentials(self):
        """Clear the credentials for 'self.app_name'."""
        yield Keyring().delete_credentials(self.app_name)

    @inlineCallbacks
    def store_credentials(self, token):
        """Store the credentials for 'self.app_name'."""
        yield Keyring().set_credentials(self.app_name, token)

    def register(self):
        """Get credentials if found else prompt the GUI to register."""
        return self._login_or_register(login_only=False)

    def login(self):
        """Get credentials if found else prompt the GUI to login."""
        return self._login_or_register(login_only=True)

    def login_email_password(self, email, password):
        """Get credentials if found else login using email and password."""
        return self._login_or_register(login_only=True,
                                       email=email, password=password)