602
641
creds = Keyring(app_name)
603
642
creds.delete_ubuntusso_attr()
643
except: # pylint: disable=W0702
605
644
logger.exception(
606
645
"problem removing credentials from keyring for %s",
610
class LoginProcessor:
611
"""Actually do the work of processing passed parameters."""
613
def __init__(self, dbus_object):
614
"""Initialize the login processor."""
615
logger.debug("Creating a LoginProcessor")
618
self.consumer_key = None
619
self.dbus_object = dbus_object
620
logger.debug("Getting configuration")
621
self.config = get_config()
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)
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)
636
from ubuntu_sso.auth import AuthorisationClient
637
client = AuthorisationClient(self.realm,
639
user_authorisation_url,
640
access_token_url, self.consumer_key,
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,
648
logger.debug("Calling auth.client.ensure_access_token in thread")
649
gobject.timeout_add_seconds(1, client.ensure_access_token)
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,
660
user_authorisation_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)
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
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",
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")
705
logger.debug("Realm '%s' is a bad realm", realm)
707
return (request_token_url, user_authorisation_url,
708
access_token_url, consumer_secret)
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:
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)
724
def got_denial(self):
725
"""Callback function when request token has been denied"""
726
self.dbus_object.AuthorizationDenied()
728
def got_no_token(self):
729
"""Callback function when access token is not in keyring."""
730
self.dbus_object.NoCredentials()
732
def got_error(self, message):
733
"""Callback function to emit an error message over DBus."""
734
self.dbus_object.OAuthError(message)
736
def __get_url(self, realm, option, actual_realm=None):
737
"""Construct a full URL from realm and a URLpath for that realm in
740
realm_to_use = actual_realm
743
urlstub = self.__get_option(realm, option)
744
return urlparse.urljoin(realm_to_use, urlstub)
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)
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:
761
# this url does not exist in default section either
762
# this shouldn't happen
763
raise NoDefaultConfigError("No default configuration for %s" % option)
766
class Login(dbus.service.Object):
767
"""Object which listens for D-Bus OAuth requests."""
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")
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")
785
self.currently_authing = True
786
self.processor.login(realm, consumer_key)
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")
797
self.currently_authing = True
798
self.processor.login(realm, consumer_key, do_login)
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)
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)
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
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
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
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
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")