~dobey/ubuntu/oneiric/ubuntu-sso-client/release-133

« back to all changes in this revision

Viewing changes to ubuntu_sso/main.py

  • Committer: Ken VanDine
  • Date: 2010-09-09 15:03:00 UTC
  • mfrom: (11.1.3 ubuntu-sso-client-0.99.6)
  • Revision ID: ken.vandine@canonical.com-20100909150300-z0lnnumytp0493tf
Tags: 0.99.6-0ubuntu1
releasing version 0.99.6-0ubuntu1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
 
3
1
# ubuntu_sso.main - main login handling interface
4
2
#
5
 
# Author: Stuart Langridge <stuart.langridge@canonical.com>
6
3
# Author: Natalia Bidart <natalia.bidart@canonical.com>
7
4
# Author: Alejandro J. Cura <alecu@canonical.com>
8
5
#
19
16
#
20
17
# You should have received a copy of the GNU General Public License along
21
18
# with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 
"""OAuth login handler.
23
 
 
24
 
A command-line utility which accepts requests for OAuth login over D-Bus,
25
 
handles the OAuth process (including adding the OAuth access token to the
26
 
gnome keyring), and then alerts the calling app (and others) with a D-Bus
27
 
signal so they can retrieve the new token.
 
19
"""Single Sign On login handler.
 
20
 
 
21
An utility which accepts requests for Ubuntu Single Sign On login over D-Bus.
 
22
 
 
23
The OAuth process is handled, including adding the OAuth access token to the
 
24
gnome keyring.
 
25
 
28
26
"""
29
27
 
 
28
import os
30
29
import re
31
 
import time
32
30
import threading
33
31
import traceback
34
32
import urllib2
35
 
import urlparse
36
33
 
37
34
import dbus.service
38
35
import gobject
39
36
 
40
 
from dbus.mainloop.glib import DBusGMainLoop
 
37
# Unable to import 'lazr.restfulclient.*'
 
38
# pylint: disable=F0401
41
39
from lazr.restfulclient.authorize import BasicHttpAuthorizer
42
40
from lazr.restfulclient.authorize.oauth import OAuthAuthorizer
43
41
from lazr.restfulclient.errors import HTTPError
44
42
from lazr.restfulclient.resource import ServiceRoot
 
43
# pylint: enable=F0401
45
44
from oauth import oauth
46
45
 
47
 
from ubuntu_sso import (DBUS_IFACE_AUTH_NAME, DBUS_IFACE_USER_NAME,
48
 
    DBUS_IFACE_CRED_NAME, DBUS_CRED_PATH, DBUS_BUS_NAME, gui)
49
 
from ubuntu_sso.config import get_config
 
46
from ubuntu_sso import DBUS_IFACE_USER_NAME, DBUS_IFACE_CRED_NAME, gui
50
47
from ubuntu_sso.keyring import Keyring, get_token_name, U1_APP_NAME
51
 
from ubuntu_sso.logger import setupLogging
52
 
logger = setupLogging("ubuntu_sso.main")
53
 
 
54
 
DBusGMainLoop(set_as_default=True)
 
48
from ubuntu_sso.logger import setup_logging
 
49
 
 
50
 
55
51
# Disable the invalid name warning, as we have a lot of DBus style names
56
 
# pylint: disable-msg=C0103
57
 
 
58
 
PING_URL = "http://edge.one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
 
52
# pylint: disable=C0103
 
53
 
 
54
 
 
55
logger = setup_logging("ubuntu_sso.main")
 
56
PING_URL = "https://edge.one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
 
57
SERVICE_URL = "https://login.ubuntu.com/api/1.0"
59
58
 
60
59
 
61
60
class NoDefaultConfigError(Exception):
99
98
 
100
99
def keyring_store_credentials(app_name, credentials):
101
100
    """Store the credentials in the keyring."""
102
 
    logger.debug('keyring_store_credentials: app_name %r.', app_name)
 
101
    logger.info('keyring_store_credentials: app_name "%s".', app_name)
103
102
    Keyring(app_name).set_ubuntusso_attr(credentials)
104
103
 
105
104
 
106
105
def keyring_get_credentials(app_name):
107
106
    """Get the credentials from the keyring or None if not there."""
108
 
    logger.debug('keyring_get_credentials: app_name %r', app_name)
109
107
    creds = Keyring(app_name).get_ubuntusso_attr()
110
 
    logger.debug('keyring_get_credentials: Keyring returned credentials? %r',
111
 
                 creds is not None)
 
108
    logger.info('keyring_get_credentials: app_name "%s", resulting credentials'
 
109
                ' is not None? %r', app_name, creds is not None)
112
110
    return creds
113
111
 
114
112
 
122
120
        else:
123
121
            self.sso_service_class = sso_service_class
124
122
 
125
 
        self.service_url = 'https://login.ubuntu.com/api/1.0'
 
123
        self.service_url = os.environ.get('USSOC_SERVICE_URL', SERVICE_URL)
126
124
 
127
125
    def _valid_email(self, email):
128
126
        """Validate the given email."""
129
 
        # XXX: to be improved as per django validation
130
127
        return email is not None and '@' in email
131
128
 
132
129
    def _valid_password(self, password):
294
291
        """The part that runs inside the thread."""
295
292
        try:
296
293
            result_cb(app_name, f())
297
 
        except Exception, e:
 
294
        except Exception, e:  # pylint: disable=W0703
298
295
            msg = "Exception while running DBus blocking code in a thread:"
299
296
            logger.exception(msg)
300
297
            error_cb(app_name, except_to_errdict(e))
304
301
class SSOLogin(dbus.service.Object):
305
302
    """Login thru the Single Sign On service."""
306
303
 
 
304
    # Operator not preceded by a space (fails with dbus decorators)
 
305
    # pylint: disable=C0322
 
306
 
307
307
    def __init__(self, bus_name,
308
308
                 sso_login_processor_class=SSOLoginProcessor,
309
309
                 sso_service_class=None):
322
322
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
323
323
    def CaptchaGenerated(self, app_name, result):
324
324
        """Signal thrown after the captcha is generated."""
 
325
        logger.debug('SSOLogin: emitting CaptchaGenerated with app_name "%s" '
 
326
                     'and result %r', app_name, result)
325
327
 
326
328
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
327
329
    def CaptchaGenerationError(self, app_name, error):
328
330
        """Signal thrown when there's a problem generating the captcha."""
 
331
        logger.debug('SSOLogin: emitting CaptchaGenerationError with '
 
332
                     'app_name "%s" and error %r', app_name, error)
329
333
 
330
334
    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
331
335
                         in_signature='ss')
341
345
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
342
346
    def UserRegistered(self, app_name, result):
343
347
        """Signal thrown when the user is registered."""
 
348
        logger.debug('SSOLogin: emitting UserRegistered with app_name "%s" '
 
349
                     'and result %r', app_name, result)
344
350
 
345
351
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
346
352
    def UserRegistrationError(self, app_name, error):
347
353
        """Signal thrown when there's a problem registering the user."""
 
354
        logger.debug('SSOLogin: emitting UserRegistrationError with '
 
355
                     'app_name "%s" and error %r', app_name, error)
348
356
 
349
357
    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
350
358
                         in_signature='sssss')
361
369
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
362
370
    def LoggedIn(self, app_name, result):
363
371
        """Signal thrown when the user is logged in."""
 
372
        logger.debug('SSOLogin: emitting LoggedIn with app_name "%s" '
 
373
                     'and result %r', app_name, result)
364
374
 
365
375
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
366
376
    def LoginError(self, app_name, error):
367
377
        """Signal thrown when there is a problem in the login."""
 
378
        logger.debug('SSOLogin: emitting LoginError with '
 
379
                     'app_name "%s" and error %r', app_name, error)
368
380
 
369
381
    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
370
382
                         in_signature='sss')
387
399
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
388
400
    def EmailValidated(self, app_name, result):
389
401
        """Signal thrown after the email is validated."""
 
402
        logger.debug('SSOLogin: emitting EmailValidated with app_name "%s" '
 
403
                     'and result %r', app_name, result)
390
404
 
391
405
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
392
406
    def EmailValidationError(self, app_name, error):
393
407
        """Signal thrown when there's a problem validating the email."""
 
408
        logger.debug('SSOLogin: emitting EmailValidationError with '
 
409
                     'app_name "%s" and error %r', app_name, error)
394
410
 
395
411
    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
396
412
                         in_signature='ssss')
407
423
 
408
424
    # request_password_reset_token signals
409
425
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
410
 
    def PasswordResetTokenSent(self, app_name, email):
 
426
    def PasswordResetTokenSent(self, app_name, result):
411
427
        """Signal thrown when the token is succesfully sent."""
 
428
        logger.debug('SSOLogin: emitting PasswordResetTokenSent with app_name '
 
429
                     '"%s" and result %r', app_name, result)
412
430
 
413
431
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
414
432
    def PasswordResetError(self, app_name, error):
415
433
        """Signal thrown when there's a problem sending the token."""
 
434
        logger.debug('SSOLogin: emitting PasswordResetError with '
 
435
                     'app_name "%s" and error %r', app_name, error)
416
436
 
417
437
    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
418
438
                         in_signature='ss')
426
446
 
427
447
    # set_new_password signals
428
448
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
429
 
    def PasswordChanged(self, app_name, email):
 
449
    def PasswordChanged(self, app_name, result):
430
450
        """Signal thrown when the token is succesfully sent."""
 
451
        logger.debug('SSOLogin: emitting PasswordChanged with app_name "%s" '
 
452
                     'and result %r', app_name, result)
431
453
 
432
454
    @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
433
455
    def PasswordChangeError(self, app_name, error):
434
456
        """Signal thrown when there's a problem sending the token."""
 
457
        logger.debug('SSOLogin: emitting PasswordChangeError with '
 
458
                     'app_name "%s" and error %r', app_name, error)
435
459
 
436
460
    @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
437
461
                         in_signature='ssss')
447
471
class SSOCredentials(dbus.service.Object):
448
472
    """DBus object that gets credentials, and login/registers if needed."""
449
473
 
 
474
    # Operator not preceded by a space (fails with dbus decorators)
 
475
    # pylint: disable=C0322
 
476
 
 
477
    def __init__(self, *args, **kwargs):
 
478
        dbus.service.Object.__init__(self, *args, **kwargs)
 
479
        self.ping_url = os.environ.get('USSOC_PING_URL', PING_URL)
 
480
 
450
481
    @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="s")
451
482
    def AuthorizationDenied(self, app_name):
452
483
        """Signal thrown when the user denies the authorization."""
 
484
        logger.info('SSOLogin: emitting AuthorizationDenied with app_name '
 
485
                    '"%s"', app_name)
453
486
 
454
487
    @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sa{ss}")
455
488
    def CredentialsFound(self, app_name, credentials):
456
489
        """Signal thrown when the credentials are found."""
 
490
        logger.info('SSOLogin: emitting CredentialsFound with app_name "%s"',
 
491
                    app_name)
457
492
 
458
493
    @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sss")
459
494
    def CredentialsError(self, app_name, error_message, detailed_error):
460
495
        """Signal thrown when there is a problem finding the credentials."""
 
496
        logger.debug('SSOCredentials: emitting CredentialsError with app_name '
 
497
                     '"%s" and error_message %r', app_name, error_message)
461
498
 
462
499
    @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME,
463
500
                         in_signature="s", out_signature="a{ss}")
464
501
    def find_credentials(self, app_name):
465
502
        """Get the credentials from the keyring or '' if not there."""
466
503
        token = keyring_get_credentials(app_name)
 
504
        logger.info('find_credentials: app_name "%s", result is {}? %s',
 
505
                    app_name, token is None)
467
506
        if token is None:
468
507
            return {}
469
508
        else:
476
515
            creds = keyring_get_credentials(app_name)
477
516
            self._ping_url(app_name, email, creds)
478
517
            self.CredentialsFound(app_name, creds)
479
 
        except Exception:
 
518
        except:  # pylint: disable=W0702
480
519
            msg = "Problem getting the credentials from the keyring."
481
520
            logger.exception(msg)
482
521
            self.CredentialsError(app_name, msg, traceback.format_exc())
493
532
 
494
533
    def _ping_url(self, app_name, email, credentials):
495
534
        """Ping the given url."""
496
 
        logger.info('Maybe pinging url %s for app_name %r', PING_URL, app_name)
 
535
        logger.info('Maybe pinging server for app_name "%s"', app_name)
497
536
        if app_name == U1_APP_NAME:
498
 
            url = PING_URL + email
 
537
            url = self.ping_url + email
499
538
            consumer = oauth.OAuthConsumer(credentials['consumer_key'],
500
539
                                           credentials['consumer_secret'])
501
540
            token = oauth.OAuthToken(credentials['token'],
506
545
            oauth_req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
507
546
                                   consumer, token)
508
547
            request = urllib2.Request(url, headers=oauth_req.to_header())
509
 
            logger.debug('Opening the ping url with urllib2.urlopen. ' \
510
 
                         'Request to: %s', request.get_full_url())
 
548
            logger.debug('Opening the ping url %s with urllib2.urlopen. ' \
 
549
                         'Request to: %s', PING_URL, request.get_full_url())
511
550
            response = urllib2.urlopen(request)
512
551
            logger.debug('Url opened. Response: %s.', response.code)
513
552
            return response.code
525
564
            gui_app.connect(gui.SIG_REGISTRATION_FAILED, self._login_error_cb)
526
565
            gui_app.connect(gui.SIG_USER_CANCELATION,
527
566
                            self._login_auth_denied_cb)
528
 
        except Exception:
 
567
        except:  # pylint: disable=W0702
529
568
            msg = '_show_login_or_register_ui failed when calling ' \
530
569
                  'gui.UbuntuSSOClientGUI(%r, %r, %r, %r, %r)'
531
570
            logger.exception(msg, app_name, tc_url, help_text,
556
595
                                 help_text, window_id)
557
596
            else:
558
597
                self.CredentialsFound(app_name, token)
559
 
        except Exception:
 
598
        except:  # pylint: disable=W0702
560
599
            msg = "Problem getting the credentials from the keyring."
561
600
            logger.exception(msg)
562
601
            self.CredentialsError(app_name, msg, traceback.format_exc())
586
625
                                 help_text, window_id)
587
626
            else:
588
627
                self.CredentialsFound(app_name, token)
589
 
        except Exception:
 
628
        except:  # pylint: disable=W0702
590
629
            msg = "Problem getting the credentials from the keyring."
591
630
            logger.exception(msg)
592
631
            self.CredentialsError(app_name, msg, traceback.format_exc())
601
640
        try:
602
641
            creds = Keyring(app_name)
603
642
            creds.delete_ubuntusso_attr()
604
 
        except Exception:
 
643
        except:  # pylint: disable=W0702
605
644
            logger.exception(
606
645
                "problem removing credentials from keyring for %s",
607
646
                app_name)
608
 
 
609
 
 
610
 
class LoginProcessor:
611
 
    """Actually do the work of processing passed parameters."""
612
 
 
613
 
    def __init__(self, dbus_object):
614
 
        """Initialize the login processor."""
615
 
        logger.debug("Creating a LoginProcessor")
616
 
        self.note1 = None
617
 
        self.realm = None
618
 
        self.consumer_key = None
619
 
        self.dbus_object = dbus_object
620
 
        logger.debug("Getting configuration")
621
 
        self.config = get_config()
622
 
 
623
 
    def login(self, realm, consumer_key, do_login=True):
624
 
        """Initiate an OAuth login"""
625
 
        logger.debug("Initiating OAuth login in LoginProcessor")
626
 
        self.realm = str(realm)  # because they are dbus.Strings, not str
627
 
        self.consumer_key = str(consumer_key)
628
 
 
629
 
        logger.debug("Obtaining OAuth urls")
630
 
        (request_token_url, user_authorisation_url,
631
 
          access_token_url, consumer_secret) = self.get_config_urls(realm)
632
 
        logger.debug("OAuth URLs are: request='%s', userauth='%s', " + \
633
 
                     "access='%s', secret='%s'", request_token_url,
634
 
                     user_authorisation_url, access_token_url, consumer_secret)
635
 
 
636
 
        from ubuntu_sso.auth import AuthorisationClient
637
 
        client = AuthorisationClient(self.realm,
638
 
                                     request_token_url,
639
 
                                     user_authorisation_url,
640
 
                                     access_token_url, self.consumer_key,
641
 
                                     consumer_secret,
642
 
                                     callback_parent=self.got_token,
643
 
                                     callback_denied=self.got_denial,
644
 
                                     callback_notoken=self.got_no_token,
645
 
                                     callback_error=self.got_error,
646
 
                                     do_login=do_login)
647
 
 
648
 
        logger.debug("Calling auth.client.ensure_access_token in thread")
649
 
        gobject.timeout_add_seconds(1, client.ensure_access_token)
650
 
 
651
 
    def clear_token(self, realm, consumer_key):
652
 
        """Remove the currently stored OAuth token from the keyring."""
653
 
        self.realm = str(realm)
654
 
        self.consumer_key = str(consumer_key)
655
 
        (request_token_url, user_authorisation_url,
656
 
          access_token_url, consumer_secret) = self.get_config_urls(self.realm)
657
 
        from ubuntu_sso.auth import AuthorisationClient
658
 
        client = AuthorisationClient(self.realm,
659
 
                                     request_token_url,
660
 
                                     user_authorisation_url,
661
 
                                     access_token_url,
662
 
                                     self.consumer_key, consumer_secret,
663
 
                                     callback_parent=self.got_token,
664
 
                                     callback_denied=self.got_denial,
665
 
                                     callback_notoken=self.got_no_token,
666
 
                                     callback_error=self.got_error)
667
 
        gobject.timeout_add_seconds(1, client.clear_token)
668
 
 
669
 
    def error_handler(self, failure):
670
 
        """Deal with errors returned from auth process"""
671
 
        logger.debug("Error returned from auth process")
672
 
        self.dbus_object.currently_authing = False  # not block future requests
673
 
 
674
 
    def get_config_urls(self, realm):
675
 
        """Look up the URLs to use in the config file"""
676
 
        logger.debug("Fetching config URLs for realm='%s'", realm)
677
 
        if self.config.has_section(realm):
678
 
            logger.debug("Realm '%s' is in config", realm)
679
 
            request_token_url = self.__get_url(realm, "request_token_url")
680
 
            user_authorisation_url = self.__get_url(realm,
681
 
              "user_authorisation_url")
682
 
            access_token_url = self.__get_url(realm, "access_token_url")
683
 
            consumer_secret = self.__get_option(realm, "consumer_secret")
684
 
        elif realm.startswith("http://localhost") and \
685
 
          self.config.has_section("http://localhost"):
686
 
            logger.debug("Realm is localhost and is in config")
687
 
            request_token_url = self.__get_url("http://localhost",
688
 
              "request_token_url", realm)
689
 
            user_authorisation_url = self.__get_url("http://localhost",
690
 
              "user_authorisation_url", realm)
691
 
            access_token_url = self.__get_url("http://localhost",
692
 
              "access_token_url", realm)
693
 
            consumer_secret = self.__get_option("http://localhost",
694
 
              "consumer_secret")
695
 
        elif self.is_valid_url(realm):
696
 
            logger.debug("Realm '%s' is not in config", realm)
697
 
            request_token_url = self.__get_url("default",
698
 
              "request_token_url", realm)
699
 
            user_authorisation_url = self.__get_url("default",
700
 
              "user_authorisation_url", realm)
701
 
            access_token_url = self.__get_url("default",
702
 
              "access_token_url", realm)
703
 
            consumer_secret = self.__get_option(realm, "consumer_secret")
704
 
        else:
705
 
            logger.debug("Realm '%s' is a bad realm", realm)
706
 
            raise BadRealmError
707
 
        return (request_token_url, user_authorisation_url,
708
 
                access_token_url, consumer_secret)
709
 
 
710
 
    def is_valid_url(self, url):
711
 
        """Simple check for URL validity"""
712
 
        # pylint: disable-msg=W0612
713
 
        scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
714
 
        if scheme and netloc:
715
 
            return True
716
 
        else:
717
 
            return False
718
 
 
719
 
    def got_token(self, access_token):
720
 
        """Callback function when access token has been retrieved"""
721
 
        logger.debug("Token retrieved, calling NewCredentials function")
722
 
        self.dbus_object.NewCredentials(self.realm, self.consumer_key)
723
 
 
724
 
    def got_denial(self):
725
 
        """Callback function when request token has been denied"""
726
 
        self.dbus_object.AuthorizationDenied()
727
 
 
728
 
    def got_no_token(self):
729
 
        """Callback function when access token is not in keyring."""
730
 
        self.dbus_object.NoCredentials()
731
 
 
732
 
    def got_error(self, message):
733
 
        """Callback function to emit an error message over DBus."""
734
 
        self.dbus_object.OAuthError(message)
735
 
 
736
 
    def __get_url(self, realm, option, actual_realm=None):
737
 
        """Construct a full URL from realm and a URLpath for that realm in
738
 
           the config file."""
739
 
        if actual_realm:
740
 
            realm_to_use = actual_realm
741
 
        else:
742
 
            realm_to_use = realm
743
 
        urlstub = self.__get_option(realm, option)
744
 
        return urlparse.urljoin(realm_to_use, urlstub)
745
 
 
746
 
    def __get_option(self, realm, option):
747
 
        """Return a specific option for that realm in
748
 
           the config file. If the realm does not exist in the config file,
749
 
           fall back to the [default] section."""
750
 
        if self.config.has_section(realm) and \
751
 
           self.config.has_option(realm, option):
752
 
            urlstub = self.config.get(realm, option)
753
 
            return urlstub
754
 
 
755
 
        # either the realm exists and this url does not, or
756
 
        # the realm doesn't exist; either way, fall back to [default] section
757
 
        urlstub = self.config.get("default", option, None)
758
 
        if urlstub is not None:
759
 
            return urlstub
760
 
 
761
 
        # this url does not exist in default section either
762
 
        # this shouldn't happen
763
 
        raise NoDefaultConfigError("No default configuration for %s" % option)
764
 
 
765
 
 
766
 
class Login(dbus.service.Object):
767
 
    """Object which listens for D-Bus OAuth requests."""
768
 
 
769
 
    def __init__(self, bus_name):
770
 
        """Initiate the Login object."""
771
 
        dbus.service.Object.__init__(self, object_path="/", bus_name=bus_name)
772
 
        self.processor = LoginProcessor(self)
773
 
        self.currently_authing = False
774
 
        logger.debug("Login D-Bus service starting up")
775
 
 
776
 
    @dbus.service.method(dbus_interface=DBUS_IFACE_AUTH_NAME,
777
 
                         in_signature='ss', out_signature='')
778
 
    def login(self, realm, consumer_key):
779
 
        """D-Bus method, to initiate an OAuth login."""
780
 
        logger.debug("login() D-Bus message received with realm='%s', " +
781
 
                     "consumer_key='%s'", realm, consumer_key)
782
 
        if self.currently_authing:
783
 
            logger.debug("Currently in the middle of OAuth: rejecting this")
784
 
            return
785
 
        self.currently_authing = True
786
 
        self.processor.login(realm, consumer_key)
787
 
 
788
 
    @dbus.service.method(dbus_interface=DBUS_IFACE_AUTH_NAME,
789
 
                         in_signature='ssb', out_signature='')
790
 
    def maybe_login(self, realm, consumer_key, do_login):
791
 
        """D-Bus method, to maybe initiate an OAuth login."""
792
 
        logger.debug("maybe_login() D-Bus message received with realm='%s', " +
793
 
                     "consumer_key='%s'", realm, consumer_key)
794
 
        if self.currently_authing:
795
 
            logger.debug("Currently in the middle of OAuth: rejecting this")
796
 
            return
797
 
        self.currently_authing = True
798
 
        self.processor.login(realm, consumer_key, do_login)
799
 
 
800
 
    @dbus.service.method(dbus_interface=DBUS_IFACE_AUTH_NAME,
801
 
                         in_signature='ss', out_signature='')
802
 
    def clear_token(self, realm, consumer_key):
803
 
        """D-Bus method, to clear the existing token."""
804
 
        self.processor.clear_token(realm, consumer_key)
805
 
 
806
 
    @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME, signature='ss')
807
 
    def NewCredentials(self, realm, consumer_key):
808
 
        """Fire D-Bus signal when the user accepts authorization."""
809
 
        logger.debug("Firing the NewCredentials signal")
810
 
        self.currently_authing = False
811
 
        return (self.processor.realm, self.processor.consumer_key)
812
 
 
813
 
    @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME)
814
 
    def AuthorizationDenied(self):
815
 
        """Fire the signal when the user denies authorization."""
816
 
        self.currently_authing = False
817
 
 
818
 
    @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME)
819
 
    def NoCredentials(self):
820
 
        """Fired when the user does not have a token in the keyring."""
821
 
        self.currently_authing = False
822
 
 
823
 
    @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME, signature='s')
824
 
    def OAuthError(self, message):
825
 
        """Fire the signal when an error needs to be propagated to the user."""
826
 
        self.currently_authing = False
827
 
        return message
828
 
 
829
 
 
830
 
def main():
831
 
    """Start everything"""
832
 
    dbus.mainloop.glib.threads_init()
833
 
    gobject.threads_init()
834
 
    logger.debug("Starting up at %s", time.asctime())
835
 
    logger.debug("Installing the Twisted glib2reactor")
836
 
    from twisted.internet import glib2reactor  # for non-GUI apps
837
 
    glib2reactor.install()
838
 
    from twisted.internet import reactor
839
 
 
840
 
    logger.debug("Creating the D-Bus service")
841
 
    Login(dbus.service.BusName(DBUS_BUS_NAME,
842
 
                               bus=dbus.SessionBus()))
843
 
    SSOLogin(dbus.service.BusName(DBUS_BUS_NAME,
844
 
                               bus=dbus.SessionBus()))
845
 
    SSOCredentials(dbus.service.BusName(DBUS_BUS_NAME,
846
 
                               bus=dbus.SessionBus()),
847
 
                   object_path=DBUS_CRED_PATH)
848
 
    # cleverness here to say:
849
 
    # am I already running (bound to this d-bus name)?
850
 
    # if so, send a signal to the already running instance
851
 
    # this means that this app can be started from an x-ubutnuone: URL
852
 
    # to kick off the signin process
853
 
    logger.debug("Starting the reactor mainloop")
854
 
    reactor.run()