Merge lp:~nataliabidart/ubuntuone-client/split-oauth into lp:ubuntuone-client

Proposed by Natalia Bidart
Status: Merged
Approved by: dobey
Approved revision: 542
Merged at revision: 543
Proposed branch: lp:~nataliabidart/ubuntuone-client/split-oauth
Merge into: lp:ubuntuone-client
Diff against target: 2363 lines (+10/-2166)
22 files modified
Makefile.am (+0/-1)
bin/ubuntuone-login (+0/-238)
bin/ubuntuone-preferences (+4/-5)
contrib/testing/testcase.py (+1/-1)
data/Makefile.am (+2/-7)
data/com.ubuntuone.Authentication.service.in (+0/-3)
data/oauth_registration.d/ubuntuone (+0/-23)
data/oauth_urls (+0/-5)
tests/oauthdesktop/__init__.py (+0/-14)
tests/oauthdesktop/test_auth.py (+0/-263)
tests/oauthdesktop/test_config.py (+0/-90)
tests/oauthdesktop/test_key_acls.py (+0/-181)
tests/oauthdesktop/test_main.py (+0/-93)
tests/test_login.py (+0/-187)
tests/test_preferences.py (+2/-0)
ubuntuone/oauthdesktop/__init__.py (+0/-16)
ubuntuone/oauthdesktop/auth.py (+0/-480)
ubuntuone/oauthdesktop/config.py (+0/-47)
ubuntuone/oauthdesktop/key_acls.py (+0/-162)
ubuntuone/oauthdesktop/logger.py (+0/-49)
ubuntuone/oauthdesktop/main.py (+0/-296)
ubuntuone/syncdaemon/dbus_interface.py (+1/-5)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-client/split-oauth
Reviewer Review Type Date Requested Status
dobey (community) Approve
Rodrigo Moya (community) Approve
Ubuntu One hackers Pending
Review via email: mp+26946@code.launchpad.net

Commit message

Split out the code for oauthdesktop.

Description of the change

Split out the code for oauthdesktop.

Until the package for ubuntu-sso-client is built, you'll need to manually add symlinks to run the tests as per:

nessita@dali:~/canonical/ubuntuone-client/split-oauth$ ls -l bin/
lrwxrwxrwx 1 nessita nessita 49 2010-06-09 15:54 ubuntu-login -> ../../../ubuntu-sso-client/trunk/bin/ubuntu-login

nessita@dali:~/canonical/ubuntuone-client/split-oauth$ ls -l .
lrwxrwxrwx 1 nessita nessita 36 2010-06-09 15:54 ubuntu -> ../../ubuntu-sso-client/trunk/ubuntu

To post a comment you must log in.
Revision history for this message
Rodrigo Moya (rodrigo-moya) wrote :

Let's first finish the renaming of the Python packages and DBus service in ubuntu-sso-client, and then let's change this branch to reflect that renaming, so that we don't need to do any further changes (at least renames) in u1-client

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

> Let's first finish the renaming of the Python packages and DBus service in
> ubuntu-sso-client, and then let's change this branch to reflect that renaming,
> so that we don't need to do any further changes (at least renames) in
> u1-client

Renamed was finished.

Revision history for this message
dobey (dobey) wrote :

The tests/test_login.py needs to be moved to the ubuntu-sso-client tree, rather than staying here. Ideally the oauthdesktop code should be fixed to not use twisted for the temporary local http server any more. And in fact, it won't need to have a local server after it is fixed to not use the browser for doing auth. Moving this test will also obviate the need for having the symlink to ubuntu-login.

We're also going to need to have the symlink creation either be automated (or unneccessary).

Given the hour though, we can discuss it more tomorrow.

review: Needs Fixing
Revision history for this message
Rodrigo Moya (rodrigo-moya) wrote :

I think we can solve the test-login thing in another branch, so approve from me

review: Approve
Revision history for this message
dobey (dobey) wrote :

Can we simplify ubuntu-sso-client to just provide the "ubuntusso" namespace, rather than the empty ubuntu namespace (which may present future conflicts) with the sso sub package?

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

> Can we simplify ubuntu-sso-client to just provide the "ubuntusso" namespace,
> rather than the empty ubuntu namespace (which may present future conflicts)
> with the sso sub package?

Name simplified to ubuntu_sso.

534. By Natalia Bidart

Merged trunk in.

535. By Natalia Bidart

Applying seconds rename for Ubuntu SSO.

536. By Natalia Bidart

Making tests no depend on sso-client tests.

537. By Natalia Bidart

Restoting FakeLogin and FakeProcessor.

538. By Natalia Bidart

Merged trunk in.

539. By Natalia Bidart

Skipping test_login.

540. By Natalia Bidart

Poiting to the righ binary.

541. By Natalia Bidart

Merged trunk in.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Package ubuntu-sso-client is being sponsored and soon to be on main. In the mean time, it can be downloaded from https://launchpad.net/~nataliabidart/+archive/ppa

The split is ready to go into trunk.

Revision history for this message
dobey (dobey) wrote :

Please just remove the test_login.py in this branch. I have a branch for ubuntu-sso-client nearly ready to propose, which moves it there.

review: Needs Fixing
542. By Natalia Bidart

Removing test_login which will be moved to ubuntu-sso-client project.

Revision history for this message
dobey (dobey) wrote :

OK. Aside from potential smaller issues that might crop up, and some possible small changes we might need to make to use ubuntu-sso-client uninstalled, this seems ok to me now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile.am'
2--- Makefile.am 2010-06-11 15:33:16 +0000
3+++ Makefile.am 2010-06-16 20:35:34 +0000
4@@ -25,7 +25,6 @@
5 bin/u1sync
6
7 libexec_SCRIPTS = \
8- bin/ubuntuone-login \
9 bin/ubuntuone-syncdaemon
10
11 manfilesdir = $(mandir)/man1
12
13=== removed file 'bin/ubuntuone-login'
14--- bin/ubuntuone-login 2010-04-14 23:31:21 +0000
15+++ bin/ubuntuone-login 1970-01-01 00:00:00 +0000
16@@ -1,238 +0,0 @@
17-#!/usr/bin/python
18-
19-# ubuntuone-login - Client side log-in utility for Ubuntu One
20-#
21-# Author: Rodney Dawes <rodney.dawes@canonical.com>
22-#
23-# Copyright 2009 Canonical Ltd.
24-#
25-# This program is free software: you can redistribute it and/or modify it
26-# under the terms of the GNU General Public License version 3, as published
27-# by the Free Software Foundation.
28-#
29-# This program is distributed in the hope that it will be useful, but
30-# WITHOUT ANY WARRANTY; without even the implied warranties of
31-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
32-# PURPOSE. See the GNU General Public License for more details.
33-#
34-# You should have received a copy of the GNU General Public License along
35-# with this program. If not, see <http://www.gnu.org/licenses/>.
36-
37-import pygtk
38-pygtk.require('2.0')
39-import gtk
40-import pango
41-import sys
42-import gettext
43-from ubuntuone import clientdefs
44-
45-import dbus.service
46-
47-from dbus.mainloop.glib import DBusGMainLoop
48-from ubuntuone.oauthdesktop.main import Login
49-
50-from ubuntuone.oauthdesktop.logger import setupLogging
51-logger = setupLogging("ubuntuone-login")
52-
53-DBusGMainLoop(set_as_default=True)
54-
55-_ = gettext.gettext
56-ngettext = gettext.ngettext
57-
58-DBUS_IFACE_AUTH_NAME = "com.ubuntuone.Authentication"
59-
60-OAUTH_REALM = "https://ubuntuone.com"
61-OAUTH_CONSUMER = "ubuntuone"
62-
63-NOTIFY_ICON_SIZE = 48
64-
65-# Why thank you GTK+ for enforcing style-set and breaking API
66-RCSTYLE = """
67-style 'dialogs' {
68- GtkDialog::action-area-border = 12
69- GtkDialog::button-spacing = 6
70- GtkDialog::content-area-border = 0
71-}
72-widget_class '*Dialog*' style 'dialogs'
73-"""
74-
75-
76-class LoginMain(object):
77- """Main login manager process class."""
78-
79- def __init__(self, *args, **kw):
80- """Initializes the child threads and dbus monitor."""
81- gtk.rc_parse_string(RCSTYLE)
82-
83- logger.info(_("Starting Ubuntu One login manager version %s") %
84- clientdefs.VERSION)
85-
86- self.__bus = dbus.SessionBus()
87-
88- def _connect_dbus_signals(self):
89- """Set up some signal handlers for DBus signals."""
90- self.__bus.add_signal_receiver(
91- handler_function=self.new_credentials,
92- signal_name="NewCredentials",
93- dbus_interface=DBUS_IFACE_AUTH_NAME)
94- self.__bus.add_signal_receiver(
95- handler_function=self.auth_denied,
96- signal_name="AuthorizationDenied",
97- dbus_interface=DBUS_IFACE_AUTH_NAME)
98- self.__bus.add_signal_receiver(
99- handler_function=self.got_oauth_error,
100- signal_name="OAuthError",
101- dbus_interface=DBUS_IFACE_AUTH_NAME)
102-
103-
104- def new_credentials(self, realm=None, consumer_key=None, sender=None):
105- """Signal callback for when we get new credentials."""
106- self.set_up_desktopcouch_pairing(consumer_key)
107-
108- def got_port(*args, **kwargs):
109- # Discard the value. We don't really care about the port, here.
110- pass
111-
112- def got_error(*args, **kwargs):
113- logger.warn("On trying to start desktopcouch-service via DBus, "
114- "we got an error. %s %s" % (args, kwargs,))
115-
116- # We have auth, so start desktopcouch service by asking for its port.
117- dc_serv_proxy = self.__bus.get_object("org.desktopcouch.CouchDB", "/")
118- dc_serv_proxy.getPort(reply_handler=got_port, error_handler=got_error)
119-
120- def auth_denied(self):
121- """Signal callback for when auth was denied by user."""
122- logger.info(_("Access to Ubuntu One was denied."))
123-
124- from twisted.internet import reactor
125- reactor.stop()
126-
127- def got_oauth_error(self, message=None):
128- """Signal callback for when an OAuth error occured."""
129- def dialog_response(dialog, response):
130- """Handle the dialog closing."""
131- dialog.destroy()
132-
133- if message:
134- logger.error(message)
135- dialog = gtk.Dialog(title=_("Ubuntu One: Error"),
136- flags=gtk.DIALOG_NO_SEPARATOR,
137- buttons=(gtk.STOCK_CLOSE,
138- gtk.RESPONSE_CLOSE))
139- dialog.set_default_response(gtk.RESPONSE_CLOSE)
140- dialog.set_icon_name("ubuntuone-client")
141-
142- area = dialog.get_content_area()
143-
144- hbox = gtk.HBox(spacing=12)
145- hbox.set_border_width(12)
146- area.pack_start(hbox)
147- hbox.show()
148-
149- image = gtk.Image()
150- image.set_from_icon_name("dialog-error", gtk.ICON_SIZE_DIALOG)
151- image.set_alignment(0.5, 0.0)
152- image.show()
153- hbox.pack_start(image, False, False)
154-
155- vbox = gtk.VBox(spacing=12)
156- vbox.show()
157- hbox.pack_start(vbox)
158-
159- label = gtk.Label("<b>%s</b>" % _("Authorization Error"))
160- label.set_use_markup(True)
161- label.set_alignment(0.0, 0.5)
162- label.show()
163- vbox.pack_start(label, False, False)
164-
165- label = gtk.Label(message)
166- label.set_line_wrap(True)
167- label.set_max_width_chars(64)
168- label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
169- label.set_alignment(0.0, 0.0)
170- label.show()
171- vbox.pack_start(label, True, True)
172-
173- dialog.connect('close', dialog_response, gtk.RESPONSE_CLOSE)
174- dialog.connect('response', dialog_response)
175-
176- dialog.show()
177- else:
178- logger.error(_("Got an OAuth error with no message."))
179-
180- def set_up_desktopcouch_pairing(self, consumer_key):
181- """Add a pairing record between desktopcouch and Ubuntu One"""
182- try:
183- from desktopcouch.pair.couchdb_pairing.couchdb_io import \
184- put_static_paired_service
185- from desktopcouch.records.server import CouchDatabase
186- except ImportError:
187- # desktopcouch is not installed
188- logger.debug(_("Not adding desktopcouch pairing since"
189- " desktopcouch is not installed"))
190- return
191- # Check whether there is already a record of the Ubuntu One service
192- db = CouchDatabase("management", create=True)
193- if not db.view_exists("ubuntu_one_pair_record","ubuntu_one_pair_record"):
194- map_js = """function(doc) {
195- if (doc.service_name == "ubuntuone") {
196- if (doc.application_annotations &&
197- doc.application_annotations["Ubuntu One"] &&
198- doc.application_annotations["Ubuntu One"]["private_application_annotations"] &&
199- doc.application_annotations["Ubuntu One"]["private_application_annotations"]["deleted"]) {
200- emit(doc._id, 1);
201- } else {
202- emit(doc._id, 0)
203- }
204- }
205- }"""
206- db.add_view("ubuntu_one_pair_record", map_js, None,
207- "ubuntu_one_pair_record")
208- results = db.execute_view("ubuntu_one_pair_record",
209- "ubuntu_one_pair_record")
210- found = False
211- # Results should contain either one row or no rows
212- # If there is one row, its value will be 0, meaning that there is
213- # already an Ubuntu One pairing record, or 1, meaning that there
214- # was an Ubuntu One pairing record but it has since been unpaired
215- # Only create a new record if there is not one already. Specifically,
216- # do not add the record if there is a deleted one, as this means
217- # that the user explicitly unpaired it!
218- for row in results:
219- found = True
220- if row.value == 1:
221- logger.debug(_("Not adding desktopcouch pairing since"
222- " the user has explicitly unpaired with Ubuntu One"))
223- else:
224- logger.debug(_("Not adding desktopcouch pairing since"
225- " we are already paired"))
226- if not found:
227- put_static_paired_service(None, "ubuntuone")
228- logger.debug(_("Pairing desktopcouch with Ubuntu One"))
229-
230- def main(self):
231- """Starts the gtk main loop."""
232- self._connect_dbus_signals()
233-
234- from twisted.internet import reactor
235- reactor.run()
236-
237-
238-if __name__ == "__main__":
239- gettext.bindtextdomain(clientdefs.GETTEXT_PACKAGE, clientdefs.LOCALEDIR)
240- gettext.textdomain(clientdefs.GETTEXT_PACKAGE)
241-
242- # Register DBus service for making sure we run only one instance
243- bus = dbus.SessionBus()
244- if bus.request_name(DBUS_IFACE_AUTH_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE) == dbus.bus.REQUEST_NAME_REPLY_EXISTS:
245- print _("Ubuntu One login manager already running, quitting")
246- sys.exit(0)
247-
248- from twisted.internet import gtk2reactor
249- gtk2reactor.install()
250-
251- login = Login(dbus.service.BusName(DBUS_IFACE_AUTH_NAME,
252- bus=dbus.SessionBus()))
253- manager = LoginMain()
254- manager.main()
255
256=== modified file 'bin/ubuntuone-preferences'
257--- bin/ubuntuone-preferences 2010-06-11 16:17:34 +0000
258+++ bin/ubuntuone-preferences 2010-06-16 20:35:34 +0000
259@@ -33,6 +33,8 @@
260 import subprocess
261 from threading import Thread
262 from oauth import oauth
263+
264+from ubuntu_sso import DBUS_IFACE_AUTH_NAME, DBUS_PATH_AUTH
265 from ubuntuone import clientdefs
266 from ubuntuone.syncdaemon.tools import SyncDaemonTool
267 from ubuntuone.api.restclient import RestClient
268@@ -68,9 +70,6 @@
269 DBUS_IFACE_CONFIG_NAME = DBUS_IFACE_NAME + ".Config"
270 DBUS_IFACE_STATUS_NAME = DBUS_IFACE_NAME + ".Status"
271
272-DBUS_IFACE_AUTH_NAME = "com.ubuntuone.Authentication"
273-DBUS_IFACE_AUTH_PATH = "/"
274-
275 # Our own DBus interface
276 PREFS_BUS_NAME = "com.ubuntuone.Preferences"
277
278@@ -104,7 +103,7 @@
279 """Make a login request to the login handling daemon."""
280 try:
281 client = bus.get_object(DBUS_IFACE_AUTH_NAME,
282- DBUS_IFACE_AUTH_PATH,
283+ DBUS_PATH_AUTH,
284 follow_name_owner_changes=True)
285 iface = dbus.Interface(client, DBUS_IFACE_AUTH_NAME)
286 iface.login('https://ubuntuone.com', 'ubuntuone',
287@@ -474,7 +473,7 @@
288 if token == local.key:
289 try:
290 client = self.bus.get_object(DBUS_IFACE_AUTH_NAME,
291- DBUS_IFACE_AUTH_PATH,
292+ DBUS_PATH_AUTH,
293 follow_name_owner_changes=True)
294 iface = dbus.Interface(client, DBUS_IFACE_AUTH_NAME)
295 iface.clear_token('https://ubuntuone.com', 'ubuntuone',
296
297=== modified file 'contrib/testing/testcase.py'
298--- contrib/testing/testcase.py 2010-05-26 21:43:27 +0000
299+++ contrib/testing/testcase.py 2010-06-16 20:35:34 +0000
300@@ -25,7 +25,7 @@
301 import shutil
302 import itertools
303
304-from ubuntuone.oauthdesktop.main import Login, LoginProcessor
305+from ubuntu_sso.main import Login, LoginProcessor
306 from ubuntuone.syncdaemon import (
307 config,
308 action_queue,
309
310=== modified file 'data/Makefile.am'
311--- data/Makefile.am 2010-06-11 15:33:16 +0000
312+++ data/Makefile.am 2010-06-16 20:35:34 +0000
313@@ -7,10 +7,7 @@
314
315 configdir = $(sysconfdir)/xdg/ubuntuone
316 config_in_files = logging.conf.in
317-config_DATA = oauth_urls syncdaemon.conf $(config_in_files:.conf.in=.conf)
318-
319-oauthdir = $(configdir)/oauth_registration.d
320-oauth_DATA = oauth_registration.d/ubuntuone
321+config_DATA = syncdaemon.conf $(config_in_files:.conf.in=.conf)
322
323 memenudir = $(datadir)/indicators/me
324 memenu_in_files = ubuntuone.menu.in
325@@ -45,7 +42,6 @@
326
327 servicedir = $(DBUS_SERVICES_DIR)
328 service_in_files = \
329- com.ubuntuone.Authentication.service.in \
330 com.ubuntuone.SyncDaemon.service.in
331 service_DATA = $(service_in_files:.service.in=.service)
332
333@@ -146,8 +142,7 @@
334 stamp-render \
335 $(config_DATA) \
336 $(apport_DATA) \
337- $(crashdb_DATA) \
338- oauth_registration.d
339+ $(crashdb_DATA)
340
341 CLEANFILES = \
342 $(memenu_DATA) \
343
344=== removed file 'data/com.ubuntuone.Authentication.service.in'
345--- data/com.ubuntuone.Authentication.service.in 2009-12-23 19:44:00 +0000
346+++ data/com.ubuntuone.Authentication.service.in 1970-01-01 00:00:00 +0000
347@@ -1,3 +0,0 @@
348-[D-BUS Service]
349-Name=com.ubuntuone.Authentication
350-Exec=@libexecdir@/ubuntuone-login
351
352=== removed directory 'data/oauth_registration.d'
353=== removed file 'data/oauth_registration.d/ubuntuone'
354--- data/oauth_registration.d/ubuntuone 2010-03-31 16:12:39 +0000
355+++ data/oauth_registration.d/ubuntuone 1970-01-01 00:00:00 +0000
356@@ -1,23 +0,0 @@
357-[storagefs-live]
358-realm = https://ubuntuone.com
359-consumer_key = ubuntuone
360-exe_path = /usr/bin/python
361-application_name = ubuntuone-syncdaemon
362-
363-[storagefs-local]
364-realm = http://localhost
365-consumer_key = ubuntuone
366-exe_path = /usr/bin/python
367-application_name = ubuntuone-syncdaemon
368-
369-[control-panel]
370-realm = https://ubuntuone.com
371-consumer_key = ubuntuone
372-exe_path = /usr/bin/python
373-application_name = ubuntuone-preferences
374-
375-[launcher]
376-realm = https://ubuntuone.com
377-consumer_key = ubuntuone
378-exe_path = /usr/bin/python
379-application_name = ubuntuone-launch
380
381=== removed file 'data/oauth_urls'
382--- data/oauth_urls 2009-05-12 13:36:05 +0000
383+++ data/oauth_urls 1970-01-01 00:00:00 +0000
384@@ -1,5 +0,0 @@
385-[default]
386-request_token_url = /oauth/request/
387-user_authorisation_url = /oauth/authorize/
388-access_token_url = /oauth/access/
389-consumer_secret=hammertime
390
391=== removed directory 'tests/oauthdesktop'
392=== removed file 'tests/oauthdesktop/__init__.py'
393--- tests/oauthdesktop/__init__.py 2009-05-12 13:36:05 +0000
394+++ tests/oauthdesktop/__init__.py 1970-01-01 00:00:00 +0000
395@@ -1,14 +0,0 @@
396-# Copyright 2009 Canonical Ltd.
397-#
398-# This program is free software: you can redistribute it and/or modify it
399-# under the terms of the GNU General Public License version 3, as published
400-# by the Free Software Foundation.
401-#
402-# This program is distributed in the hope that it will be useful, but
403-# WITHOUT ANY WARRANTY; without even the implied warranties of
404-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
405-# PURPOSE. See the GNU General Public License for more details.
406-#
407-# You should have received a copy of the GNU General Public License along
408-# with this program. If not, see <http://www.gnu.org/licenses/>.
409-"""Tests for OAuth library"""
410
411=== removed file 'tests/oauthdesktop/test_auth.py'
412--- tests/oauthdesktop/test_auth.py 2010-01-11 17:46:00 +0000
413+++ tests/oauthdesktop/test_auth.py 1970-01-01 00:00:00 +0000
414@@ -1,263 +0,0 @@
415-# test_auth - Tests for ubuntuone.oauthdesktop.auth module
416-#
417-# Author: Stuart Langridge <stuart.langridge@canonical.com>
418-#
419-# Copyright 2009 Canonical Ltd.
420-#
421-# This program is free software: you can redistribute it and/or modify it
422-# under the terms of the GNU General Public License version 3, as published
423-# by the Free Software Foundation.
424-#
425-# This program is distributed in the hope that it will be useful, but
426-# WITHOUT ANY WARRANTY; without even the implied warranties of
427-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
428-# PURPOSE. See the GNU General Public License for more details.
429-#
430-# You should have received a copy of the GNU General Public License along
431-# with this program. If not, see <http://www.gnu.org/licenses/>.
432-"""Tests for the OAuth client code for StorageFS."""
433-
434-import gnomekeyring
435-from contrib.mocker import ANY, IN, MockerTestCase
436-import testresources
437-
438-from oauth import oauth
439-from twisted.trial.unittest import TestCase as TwistedTestCase
440-from ubuntuone.oauthdesktop.auth import (AuthorisationClient,
441- NoAccessToken)
442-
443-
444-# Adding TwistedTestCase as parent to be able to skip
445-# test_ensure_access_token_no_token
446-class AuthorisationClientTests(MockerTestCase, TwistedTestCase):
447- """Test the GNOME keyring integration portions of the auth code."""
448-
449- def setUp(self):
450- """Sets up a mock keyring."""
451- MockerTestCase.setUp(self)
452- self.keyring = self.mocker.mock()
453- self.client = None
454- self.item = self.mocker.mock(gnomekeyring.Found)
455-
456- self.item_id = 999
457-
458- ex = self.expect(self.item.item_id)
459- ex.result(self.item_id)
460- ex.count(0, None)
461-
462- ex = self.expect(self.item.secret)
463- ex.result('oauth_token=access_key&oauth_token_secret=access_secret')
464- ex.count(0, None)
465-
466- def expect_token_query(self):
467- """Expects the keyring to be queried for a token."""
468- return self.expect(
469- self.keyring.find_items_sync(
470- gnomekeyring.ITEM_GENERIC_SECRET,
471- {'ubuntuone-realm': 'realm',
472- 'oauth-consumer-key': 'consumer_key'})
473- )
474-
475- def expect_token_store(self):
476- """Expects the token to be stored in the keyring."""
477- return self.expect(self.keyring.item_create_sync(
478- None, gnomekeyring.ITEM_GENERIC_SECRET,
479- 'UbuntuOne token for realm',
480- {'ubuntuone-realm': 'realm',
481- 'oauth-consumer-key': 'consumer_key'},
482- # Either order for the token and secret is valid
483- IN(['oauth_token=access_key&oauth_token_secret=access_secret',
484- 'oauth_token_secret=access_secret&oauth_token=access_key']),
485- True))
486-
487- def expect_token_store_denied(self):
488- """Expects the token to be denied for storing in keyring."""
489- self.expect_token_store().throw(gnomekeyring.DeniedError)
490-
491- def mock_has_token(self):
492- """Mocks a cached token in the keyring."""
493- self.expect_token_query().result([self.item])
494-
495- def mock_no_token(self, exception):
496- """Mocks no token in the keyring."""
497- self.expect_token_query().throw(exception)
498-
499- def replay(self, callback_parent=None, callback_denied=None,
500- do_login=True):
501- """Starts the replay phase and sets up a client object to be tested,
502- wired up to the mock keyring.
503-
504- """
505- self.mocker.replay()
506- self.client = AuthorisationClient(
507- 'realm', 'request_token_url', 'user_authorisation_url',
508- 'access_token_url', 'consumer_key', 'consumer_secret',
509- callback_parent, callback_denied, do_login, keyring=self.keyring)
510-
511- def test_get_access_token(self):
512- """The get_access_token method returns the access token"""
513- self.mock_has_token()
514- self.replay()
515- token = self.client.get_access_token()
516- self.assertTrue(isinstance(token, oauth.OAuthToken))
517- self.assertEqual(token.key, 'access_key')
518- self.assertEqual(token.secret, 'access_secret')
519-
520- def test_get_access_token_no_match(self):
521- """The get_access_token method fails if there are no matching items"""
522- self.mock_no_token(gnomekeyring.NoMatchError)
523- self.replay()
524- self.assertRaises(NoAccessToken, self.client.get_access_token)
525-
526- def test_get_access_token_denied(self):
527- """The get_access_token method fails if access is denied"""
528- self.mock_no_token(gnomekeyring.DeniedError)
529- self.replay()
530- self.assertRaises(NoAccessToken, self.client.get_access_token)
531-
532- def test_have_access_token(self):
533- """The `have_access_token` method returns True if a the
534- keyring contains a token."""
535- self.mock_has_token()
536- self.replay()
537- self.assertEqual(self.client.have_access_token(), True)
538-
539- def test_have_access_token_fail(self):
540- """The `have_access_token` method returns False if the keyring
541- does not contain a token."""
542- self.mock_no_token(gnomekeyring.NoMatchError)
543- self.replay()
544- self.assertEqual(self.client.have_access_token(), False)
545-
546- def test_store_token(self):
547- """The store_token method correctly stores an item in the keyring,
548- and correctly sets an ACL on it."""
549- self.expect_token_store().result(self.item_id)
550- saka = self.mocker.replace(
551- "ubuntuone.oauthdesktop.key_acls.set_all_key_acls")
552- saka(item_id=self.item_id)
553- self.mocker.result(None)
554-
555- sleep = self.mocker.replace("time.sleep")
556- sleep(4)
557- self.mocker.result(None)
558-
559- self.replay()
560- self.client.store_token(oauth.OAuthToken('access_key', 'access_secret'))
561-
562- def test_store_token_denied(self):
563- """The store_token method correctly stores an item in the keyring,
564- and correctly sets an ACL on it."""
565- self.expect_token_store_denied()
566-
567- self.replay()
568- self.client.store_token(oauth.OAuthToken('access_key', 'access_secret'))
569-
570- def test_clear_existing_token(self):
571- """Makes sure that clear token clears an existing token."""
572- self.mock_has_token()
573- self.expect(self.keyring.item_delete_sync(None, self.item_id))
574- self.replay()
575- self.client.clear_token()
576-
577- def test_clear_no_existing_token(self):
578- """Makes sure that clear with no existing token still works."""
579- self.mock_no_token(gnomekeyring.NoMatchError)
580- self.replay()
581- self.client.clear_token()
582-
583- def test_ensure_access_token(self):
584- """If the user already has a token, no new token is requested."""
585- self.mock_has_token()
586- callback_function = self.mocker.mock()
587- callback_function(ANY)
588- self.replay(callback_parent=callback_function)
589- self.client.ensure_access_token()
590-
591- def test_ensure_access_token_no_token(self):
592- """If the user has no token, a new one is requested and stored, via
593- an OAuth callback to the internal webserver."""
594-
595- self.mock_no_token(gnomekeyring.NoMatchError)
596-
597- request_token = self.mocker.mock()
598- self.expect(request_token.key).result('access_key').count(0, None)
599- ex = self.expect(request_token.secret)
600- ex.result('access_secret').count(0, None)
601- make_token_request = self.mocker.mock()
602- self.expect(make_token_request(ANY)).result(request_token)
603-
604- open_in_browser = self.mocker.mock()
605- open_in_browser(ANY)
606-
607- ri = self.mocker.replace("random.randint")
608- ri(1000000, 10000000)
609- self.mocker.result(12345678)
610- get_temporary_httpd = self.mocker.mock()
611- get_temporary_httpd(12345678, ANY, True)
612- self.mocker.result("http://callbackurl")
613-
614- self.replay(callback_parent=lambda a: None)
615-
616- self.client.make_token_request = make_token_request
617- self.client.open_in_browser = open_in_browser
618- self.client.get_temporary_httpd = get_temporary_httpd
619- # skip the "are we online, via networkmanager" bit
620- self.client.acquire_access_token_if_online = \
621- self.client.acquire_access_token
622- self.client.ensure_access_token()
623- test_ensure_access_token_no_token.skip = \
624- "Fails with exceptions.AssertionError: [Mocker] Unmet expectations, "\
625- "see bug #488933"
626-
627-
628-class AcquireAccessTokenTests(testresources.ResourcedTestCase):
629- """OAuth token acquisition tests."""
630-
631- def setUp(self):
632- super(AcquireAccessTokenTests, self).setUp()
633-
634- def tearDown(self):
635- super(AcquireAccessTokenTests, self).tearDown()
636-
637- def test_acquire_access_token(self):
638- """Test that acquire_access_token() can acquire the access token."""
639-
640- def make_token_request(oauth_request):
641- """Make an OAuth token request via the test browser."""
642- return oauth.OAuthToken.from_string("oauth_token=access_token&" +
643- "oauth_token_secret=" +
644- "access_secret")
645-
646- def open_in_browser(url):
647- """Just return as we aren't subscribed to the page."""
648- return
649-
650- def got_token(token):
651- """Called with the token once auth is completed"""
652- self.assertTrue(isinstance(token, oauth.OAuthToken))
653-
654- def get_temporary_httpd(nonce, retrieve_function, store):
655- """Mock the temporary httpd and return a callback URL"""
656- # returns callback URL; this is an invalid URL, of course,
657- # (port is too high) but we check later that the mechanize
658- # browser tries to navigate there
659- return "http://localhost:99999/?nonce=99999"
660-
661- def store_token(token):
662- """Don't use the keyring; do nothing"""
663- pass
664-
665- client = AuthorisationClient(
666- 'http://ubuntuone/',
667- 'http://ubuntuone/oauth/request/',
668- 'http://ubuntuone/oauth/authorize/',
669- 'http://ubuntuone/oauth/access/',
670- 'consumer_key', 'consumer_secret',
671- callback_parent=got_token)
672- client.make_token_request = make_token_request
673- client.open_in_browser = open_in_browser
674- client.get_temporary_httpd = get_temporary_httpd
675- client.store_token = store_token
676-
677- client.acquire_access_token('token description')
678
679=== removed file 'tests/oauthdesktop/test_config.py'
680--- tests/oauthdesktop/test_config.py 2009-07-02 20:22:51 +0000
681+++ tests/oauthdesktop/test_config.py 1970-01-01 00:00:00 +0000
682@@ -1,90 +0,0 @@
683-# test_config - tests for ubuntuone.oauthdesktop.config
684-#
685-# Author: Stuart Langridge <stuart.langridge@canonical.com>
686-#
687-# Copyright 2009 Canonical Ltd.
688-#
689-# This program is free software: you can redistribute it and/or modify it
690-# under the terms of the GNU General Public License version 3, as published
691-# by the Free Software Foundation.
692-#
693-# This program is distributed in the hope that it will be useful, but
694-# WITHOUT ANY WARRANTY; without even the implied warranties of
695-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
696-# PURPOSE. See the GNU General Public License for more details.
697-#
698-# You should have received a copy of the GNU General Public License along
699-# with this program. If not, see <http://www.gnu.org/licenses/>.
700-"""Tests for the OAuth client code for StorageFS."""
701-
702-import os, StringIO
703-from contrib.mocker import MockerTestCase
704-from ubuntuone.oauthdesktop import config
705-from ubuntuone.oauthdesktop.config import get_config
706-
707-class ConfigFileTests(MockerTestCase):
708- """Test the config file finder"""
709-
710- def test_get_file_from_tmp(self):
711- """Does the tmp file get chosen?"""
712-
713- # mock that tmp config file exists
714- tmp_config_file = os.path.realpath(os.path.join(
715- os.path.split(config.__file__)[0], "../../data/oauth_urls"
716- ))
717- osp = self.mocker.replace("os.path")
718- osp.isfile(tmp_config_file)
719- self.mocker.result(True)
720- self.mocker.replay()
721-
722- conf = get_config()
723- self.assertEqual(conf.FILENAME, tmp_config_file)
724-
725- def test_get_file_from_home(self):
726- """Does our home config file get chosen?"""
727-
728- # mock that home config file exists
729- home_config_file = os.path.expanduser("~/.config/ubuntuone/oauth_urls")
730- osp = self.mocker.replace("os.path")
731- osp.exists(home_config_file)
732- self.mocker.result(True)
733- self.mocker.replay()
734-
735- conf = get_config(use_tmpconfig=False) # pretend not in source tree
736- self.assertEqual(conf.FILENAME, home_config_file)
737-
738- def test_get_file_from_etc(self):
739- """Does our /etc config file get chosen?"""
740-
741- # mock that etc config file exists
742- etc_config_file = os.path.expanduser("/etc/xdg/ubuntuone/oauth_urls")
743- osp = self.mocker.replace("os.path")
744- osp.exists(etc_config_file)
745- self.mocker.result(True)
746- self.mocker.replay()
747-
748- conf = get_config(use_tmpconfig=False) # pretend not in source tree
749- self.assertEqual(conf.FILENAME, etc_config_file)
750-
751- def test_tmp_file_parses(self):
752- """Does the tmp file get chosen and parse correctly?"""
753-
754- # mock that tmp config file exists
755- tmp_config_file = os.path.realpath(os.path.join(
756- os.path.split(config.__file__)[0], "../../data/oauth_urls"
757- ))
758- osp = self.mocker.replace("os.path")
759- osp.isfile(tmp_config_file)
760- self.mocker.result(True)
761-
762- sio = StringIO.StringIO("[default]\n")
763- mock_open = self.mocker.replace(open)
764- mock_open(tmp_config_file)
765- self.mocker.result(sio)
766-
767- self.mocker.replay()
768-
769- conf = get_config()
770- self.assertTrue(conf.has_section("default"))
771-
772-
773
774=== removed file 'tests/oauthdesktop/test_key_acls.py'
775--- tests/oauthdesktop/test_key_acls.py 2009-06-26 17:01:42 +0000
776+++ tests/oauthdesktop/test_key_acls.py 1970-01-01 00:00:00 +0000
777@@ -1,181 +0,0 @@
778-# test_key_acls - tests for ubuntuone.oauthdesktop.key_acls
779-#
780-# Author: Stuart Langridge <stuart.langridge@canonical.com>
781-#
782-# Copyright 2009 Canonical Ltd.
783-#
784-# This program is free software: you can redistribute it and/or modify it
785-# under the terms of the GNU General Public License version 3, as published
786-# by the Free Software Foundation.
787-#
788-# This program is distributed in the hope that it will be useful, but
789-# WITHOUT ANY WARRANTY; without even the implied warranties of
790-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
791-# PURPOSE. See the GNU General Public License for more details.
792-#
793-# You should have received a copy of the GNU General Public License along
794-# with this program. If not, see <http://www.gnu.org/licenses/>.
795-"""Tests for the ACL setting code for ubuntuone-oauth-login."""
796-
797-import os, StringIO
798-import xdg.BaseDirectory
799-from contrib.mocker import MockerTestCase, IN, ANY
800-from ubuntuone.oauthdesktop.key_acls import (
801- get_privileged_config_folder, get_acl_preregistrations, set_all_key_acls,
802- set_single_acl)
803-
804-class Loader(MockerTestCase):
805- """Confirm only the /etc/xdg config files are loaded"""
806-
807- def test_etc_found(self):
808- """Is the /etc/xdg folder found if present?"""
809-
810- ETC_FOLDER = "/etc/xdg/ubuntuone"
811- osp = self.mocker.replace("os.path")
812- osp.exists(ETC_FOLDER)
813- self.mocker.result(True)
814- self.mocker.replay()
815-
816- self.assertEqual(ETC_FOLDER,
817- get_privileged_config_folder(use_source_tree_folder=False))
818-
819- def test_etc_found_despite_home(self):
820- """Is the /etc/xdg folder found even if the $HOME folder is present?"""
821-
822- ETC_FOLDER = "/etc/xdg/ubuntuone"
823- HOME_FOLDER = os.path.join(xdg.BaseDirectory.xdg_config_home,
824- "ubuntuone")
825- osp = self.mocker.replace("os.path")
826- osp.exists(ETC_FOLDER)
827- self.mocker.result(True)
828- osp.exists(HOME_FOLDER)
829- self.mocker.result(True)
830- self.mocker.replay()
831-
832- self.assertEqual(ETC_FOLDER,
833- get_privileged_config_folder(use_source_tree_folder=False))
834-
835- def test_etc_files_found(self):
836- """Are files in /etc found?"""
837- ETC_FOLDER = "/etc/xdg/ubuntuone"
838- ETC_FILES_FOLDER = "/etc/xdg/ubuntuone/oauth_registration.d"
839- FILE_LIST = ["a", "b"]
840- osp = self.mocker.replace("os.path")
841- osp.exists(ETC_FOLDER)
842- self.mocker.result(True)
843- osp.isdir(ETC_FILES_FOLDER)
844- self.mocker.result(True)
845- listdir = self.mocker.replace("os.listdir")
846- listdir(ETC_FILES_FOLDER)
847- self.mocker.result(FILE_LIST)
848- self.mocker.replay()
849-
850- our_list = sorted(
851- [os.path.join(ETC_FILES_FOLDER, x) for x in FILE_LIST])
852- their_list = sorted(
853- get_acl_preregistrations(use_source_tree_folder=False))
854- self.assertEqual(our_list, their_list)
855-
856- def test_set_single_acl(self):
857- """Does set_single_acl work?"""
858-
859- giifr = self.mocker.replace(
860- "ubuntuone.oauthdesktop.key_acls.get_item_ids_for_realm")
861- igas = self.mocker.replace("gnomekeyring.item_get_acl_sync")
862- isas = self.mocker.replace("gnomekeyring.item_set_acl_sync")
863- acon = self.mocker.replace("gnomekeyring.AccessControl")
864- self.expect(giifr("realm", "consumer_key")).result([999])
865- self.expect(igas(None, 999)).result([])
866- ac1 = self.mocker.mock()
867- self.expect(acon(ANY, ANY)).result(ac1)
868- ac1.set_display_name("application_name")
869- ac1.set_path_name("/tmp/exe_path")
870- self.expect(isas(None, 999, [ac1])).result(None)
871- self.expect(giifr("realm2", "consumer_key2")).result([9999])
872- self.expect(igas(None, 9999)).result([])
873- ac2 = self.mocker.mock()
874- self.expect(acon(ANY, ANY)).result(ac2)
875- ac2.set_display_name("application_name2")
876- ac2.set_path_name("/tmp/exe_path2")
877- self.expect(isas(None, 9999, [ac2])).result(None)
878- self.mocker.replay()
879-
880- set_single_acl([
881- ("realm", "consumer_key", "/tmp/exe_path", "application_name"),
882- ("realm2", "consumer_key2", "/tmp/exe_path2", "application_name2"),
883- ])
884-
885-
886- def test_etc_files_parsed(self):
887- """Are files in /etc parsed correctly?"""
888-
889- ETC_FOLDER = "/etc/xdg/ubuntuone"
890- ETC_FILES_FOLDER = "/etc/xdg/ubuntuone/oauth_registration.d"
891- FILE_LIST = ["a", "b"]
892- osp = self.mocker.replace("os.path")
893- osp.exists(ETC_FOLDER)
894- self.mocker.result(True)
895- osp.isdir(ETC_FILES_FOLDER)
896- self.mocker.result(True)
897- listdir = self.mocker.replace("os.listdir")
898- listdir(ETC_FILES_FOLDER)
899- self.mocker.result(FILE_LIST)
900-
901- sio1 = StringIO.StringIO("""[app1]
902-realm = https://realm.example.com/
903-consumer_key = example_key
904-exe_path = /nowhere/executable/path
905-application_name = example_app_name
906-
907-[app2]
908-realm = https://other.example.com/
909-consumer_key = example_key
910-exe_path = /nowhere/executable/path
911-application_name = example_app_name
912-
913-""")
914- sio2 = StringIO.StringIO("""[app3]
915-exe_path = /nowhere/path/2
916-application_name = example_app_name2
917-consumer_key = example_key2
918-realm = https://realm2.example.com/
919-""")
920- mock_open = self.mocker.replace(open)
921- mock_open(os.path.join(ETC_FILES_FOLDER, "a"))
922- self.mocker.result(sio1)
923- mock_open(os.path.join(ETC_FILES_FOLDER, "b"))
924- self.mocker.result(sio2)
925-
926- ssa = self.mocker.replace(
927- "ubuntuone.oauthdesktop.key_acls.set_single_acl")
928- # list may come up in any order
929- ssa(IN([
930- [
931- ("https://realm.example.com/", "example_key",
932- "/nowhere/executable/path", "example_app_name"),
933- ("https://other.example.com/", "example_key",
934- "/nowhere/executable/path", "example_app_name"),
935- ],
936- [
937- ("https://other.example.com/", "example_key",
938- "/nowhere/executable/path", "example_app_name"),
939- ("https://realm.example.com/", "example_key",
940- "/nowhere/executable/path", "example_app_name"),
941- ],
942- ]), specific_item_id=None
943- )
944- self.mocker.result(None)
945- ssa([
946- ("https://realm2.example.com/", "example_key2",
947- "/nowhere/path/2", "example_app_name2")
948- ], specific_item_id=None)
949- self.mocker.result(None)
950- self.mocker.replay()
951-
952- set_all_key_acls(use_source_tree_folder=False)
953-
954-
955-
956-
957-
958-
959
960=== removed file 'tests/oauthdesktop/test_main.py'
961--- tests/oauthdesktop/test_main.py 2009-06-26 17:01:42 +0000
962+++ tests/oauthdesktop/test_main.py 1970-01-01 00:00:00 +0000
963@@ -1,93 +0,0 @@
964-# test_main - tests for ubuntuone.oauthdesktop.main
965-#
966-# Author: Stuart Langridge <stuart.langridge@canonical.com>
967-#
968-# Copyright 2009 Canonical Ltd.
969-#
970-# This program is free software: you can redistribute it and/or modify it
971-# under the terms of the GNU General Public License version 3, as published
972-# by the Free Software Foundation.
973-#
974-# This program is distributed in the hope that it will be useful, but
975-# WITHOUT ANY WARRANTY; without even the implied warranties of
976-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
977-# PURPOSE. See the GNU General Public License for more details.
978-#
979-# You should have received a copy of the GNU General Public License along
980-# with this program. If not, see <http://www.gnu.org/licenses/>.
981-"""Tests for the OAuth client code for StorageFS."""
982-
983-import os, StringIO
984-from contrib.mocker import MockerTestCase
985-from ubuntuone.oauthdesktop import config
986-from ubuntuone.oauthdesktop.main import LoginProcessor, BadRealmError
987-
988-class Realm(MockerTestCase):
989- """Test the realm handling finder"""
990-
991- def test_invalid_realm(self):
992- """Are invalid realms rejected?"""
993-
994- login = LoginProcessor(None, use_libnotify=False)
995- self.assertRaises(BadRealmError, login.login, "bad realm", "key")
996-
997- def test_realms(self):
998- """Are realm URLs correctly retrieved from the config?"""
999-
1000- # mock that tmp config file exists
1001- tmp_config_file = os.path.realpath(os.path.join(
1002- os.path.split(config.__file__)[0], "../../data/oauth_urls"
1003- ))
1004- osp = self.mocker.replace("os.path")
1005- osp.isfile(tmp_config_file)
1006- self.mocker.result(True)
1007-
1008- sio = StringIO.StringIO("""[default]
1009-request_token_url = /rtu-default
1010-user_authorisation_url = /uau-default
1011-access_token_url = /atu-default
1012-consumer_secret = foo-default
1013-
1014-[http://localhost]
1015-request_token_url = /rtu-localhost
1016-user_authorisation_url = /uau-localhost
1017-access_token_url = /atu-localhost
1018-consumer_secret = foo-localhost
1019-
1020-[http://ubuntuone.com]
1021-request_token_url = /rtu-ubuntuone
1022-user_authorisation_url = /uau-ubuntuone
1023-access_token_url = /atu-ubuntuone
1024-consumer_secret = foo-ubuntuone
1025-
1026-""")
1027- mock_open = self.mocker.replace(open)
1028- mock_open(tmp_config_file)
1029- self.mocker.result(sio)
1030-
1031- self.mocker.replay()
1032-
1033- login = LoginProcessor(None, use_libnotify=False)
1034-
1035- # are localhost:XXXX URLs correctly fetched?
1036- (rtu, uau, atu, cs) = login.get_config_urls("http://localhost:9876")
1037- self.assertEqual(rtu, "http://localhost:9876/rtu-localhost")
1038- self.assertEqual(uau, "http://localhost:9876/uau-localhost")
1039- self.assertEqual(atu, "http://localhost:9876/atu-localhost")
1040- self.assertEqual(cs, "foo-localhost")
1041-
1042- # is a URL specifically in the config correctly fetched?
1043- (rtu, uau, atu, cs) = login.get_config_urls("http://ubuntuone.com")
1044- self.assertEqual(rtu, "http://ubuntuone.com/rtu-ubuntuone")
1045- self.assertEqual(uau, "http://ubuntuone.com/uau-ubuntuone")
1046- self.assertEqual(atu, "http://ubuntuone.com/atu-ubuntuone")
1047- self.assertEqual(cs, "foo-ubuntuone")
1048-
1049- # is a URL not in the config correctly fetched as default?
1050- (rtu, uau, atu, cs) = login.get_config_urls("http://other.example.net")
1051- self.assertEqual(rtu, "http://other.example.net/rtu-default")
1052- self.assertEqual(uau, "http://other.example.net/uau-default")
1053- self.assertEqual(atu, "http://other.example.net/atu-default")
1054- self.assertEqual(cs, "foo-default")
1055-
1056-
1057
1058=== removed file 'tests/test_login.py'
1059--- tests/test_login.py 2010-01-15 16:32:12 +0000
1060+++ tests/test_login.py 1970-01-01 00:00:00 +0000
1061@@ -1,187 +0,0 @@
1062-# -*- coding: utf-8 -*-
1063-#
1064-# Author: Rodney Dawes <rodney.dawes@canonical.com>
1065-#
1066-# Copyright 2010 Canonical Ltd.
1067-#
1068-# This program is free software: you can redistribute it and/or modify it
1069-# under the terms of the GNU General Public License version 3, as published
1070-# by the Free Software Foundation.
1071-#
1072-# This program is distributed in the hope that it will be useful, but
1073-# WITHOUT ANY WARRANTY; without even the implied warranties of
1074-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1075-# PURPOSE. See the GNU General Public License for more details.
1076-#
1077-# You should have received a copy of the GNU General Public License along
1078-# with this program. If not, see <http://www.gnu.org/licenses/>.
1079-""" Tests for the ubuntuone-login script """
1080-
1081-import dbus.service
1082-import new
1083-import os
1084-
1085-from contrib.testing.testcase import DBusTwistedTestCase, FakeLogin
1086-from twisted.internet import defer
1087-from twisted.python.failure import Failure
1088-from ubuntuone.syncdaemon import dbus_interface
1089-
1090-class InvalidSignalError(Exception):
1091- """Exception for when we get the wrong signal called."""
1092- pass
1093-
1094-class LoginTests(DBusTwistedTestCase):
1095- """Basic tests for the ubuntuone-login script """
1096-
1097- _path = os.path.join(os.getcwd(), "bin", "ubuntuone-login")
1098- u1login = new.module('u1login')
1099- execfile(_path, u1login.__dict__)
1100-
1101- def setUp(self):
1102- DBusTwistedTestCase.setUp(self)
1103- self.oauth = FakeLogin(self.bus)
1104- self._old_path = dbus_interface.DBUS_PATH_AUTH
1105- dbus_interface.DBUS_PATH_AUTH = '/oauthdesktop'
1106-
1107- def tearDown(self):
1108- # collect all signal receivers registered during the test
1109- signal_receivers = set()
1110- with self.bus._signals_lock:
1111- for group in self.bus._signal_recipients_by_object_path.values():
1112- for matches in group.values():
1113- for match in matches.values():
1114- signal_receivers.update(match)
1115- d = self.cleanup_signal_receivers(signal_receivers)
1116- def shutdown(r):
1117- self.oauth.shutdown()
1118- dbus_interface.DBUS_PATH_AUTH = self._old_path
1119- d.addBoth(shutdown)
1120- d.addBoth(lambda _: DBusTwistedTestCase.tearDown(self))
1121- return d
1122-
1123- def test_new_credentials(self):
1124- """ Test logging in """
1125- def new_creds(realm=None, consumer_key=None, sender=None):
1126- """ Override the callback """
1127- d.callback(True)
1128-
1129- def auth_denied():
1130- """ Override the callback """
1131- d.errback(Failure(InvalidSignalError()))
1132-
1133- def got_oauth_error(message=None):
1134- """ Override the callback """
1135- d.errback(Failure(InvalidSignalError()))
1136-
1137- def set_up_desktopcouch_pairing(consumer_key):
1138- """ Override the method """
1139- return
1140-
1141- def main():
1142- """ Override LoginMain.main """
1143- return
1144-
1145- login = self.u1login.LoginMain()
1146- login.main = main
1147- login.new_credentials = new_creds
1148- login.auth_denied = auth_denied
1149- login.got_oauth_error = got_oauth_error
1150- login.set_up_desktopcouch_pairing = set_up_desktopcouch_pairing
1151-
1152- login._connect_dbus_signals()
1153-
1154- client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME,
1155- '/oauthdesktop',
1156- follow_name_owner_changes=True)
1157- d = defer.Deferred()
1158-
1159- def login_handler():
1160- """ login handler """
1161- return
1162-
1163- iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME)
1164- iface.login('http://localhost', self.u1login.OAUTH_CONSUMER,
1165- reply_handler=login_handler,
1166- error_handler=self.error_handler)
1167- return d
1168-
1169- def test_auth_denied(self):
1170- """ Test that denying authorization works correctly. """
1171-
1172- def new_creds(realm=None, consumer_key=None, sender=None):
1173- """ Override the callback """
1174- d.errback(Failure(InvalidSignalError()))
1175-
1176- def auth_denied():
1177- """ Override the callback """
1178- d.callback(True)
1179-
1180- def got_oauth_error(message=None):
1181- """ override the callback """
1182- d.errback(Failure(InvalidSignalError()))
1183-
1184- login = self.u1login.LoginMain()
1185- login.main = self.main
1186- login.new_credentials = new_creds
1187- login.auth_denied = auth_denied
1188- login.got_oauth_error = got_oauth_error
1189-
1190- login._connect_dbus_signals()
1191-
1192- self.oauth.processor.next_login_with(self.oauth.processor.got_denial)
1193-
1194- client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME,
1195- '/oauthdesktop',
1196- follow_name_owner_changes=True)
1197- d = defer.Deferred()
1198-
1199- def login_handler():
1200- """ login handler """
1201- return
1202-
1203- iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME)
1204- iface.login('http://localhost', self.u1login.OAUTH_CONSUMER,
1205- reply_handler=login_handler,
1206- error_handler=self.error_handler)
1207- return d
1208-
1209- def test_oauth_error(self):
1210- """ Test that getting an error works correctly. """
1211-
1212- def new_creds(realm=None, consumer_key=None, sender=None):
1213- """ Override the callback """
1214- d.errback(Failure(InvalidSignalError()))
1215-
1216- def auth_denied():
1217- """ Override the callback """
1218- d.errback(Failure(InvalidSignalError()))
1219-
1220- def got_oauth_error(message=None):
1221- """ override the callback """
1222- d.callback(True)
1223-
1224- login = self.u1login.LoginMain()
1225- login.main = self.main
1226- login.new_credentials = new_creds
1227- login.auth_denied = auth_denied
1228- login.got_oauth_error = got_oauth_error
1229-
1230- login._connect_dbus_signals()
1231-
1232- self.oauth.processor.next_login_with(self.oauth.processor.got_error,
1233- ('error!',))
1234-
1235- client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME,
1236- '/oauthdesktop',
1237- follow_name_owner_changes=True)
1238- d = defer.Deferred()
1239-
1240- def login_handler():
1241- """ login handler """
1242- return
1243-
1244- iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME)
1245- iface.login('http://localhost', self.u1login.OAUTH_CONSUMER,
1246- reply_handler=login_handler,
1247- error_handler=self.error_handler)
1248- return d
1249
1250=== modified file 'tests/test_preferences.py'
1251--- tests/test_preferences.py 2010-06-15 09:16:48 +0000
1252+++ tests/test_preferences.py 2010-06-16 20:35:34 +0000
1253@@ -498,3 +498,5 @@
1254 error_handler=got_dbus_error)
1255
1256 return d
1257+ test_login_check.skip = \
1258+ "This shouldn't depend on the real DBus service (see bug #591340)."
1259
1260=== removed directory 'ubuntuone/oauthdesktop'
1261=== removed file 'ubuntuone/oauthdesktop/__init__.py'
1262--- ubuntuone/oauthdesktop/__init__.py 2009-06-26 17:01:42 +0000
1263+++ ubuntuone/oauthdesktop/__init__.py 1970-01-01 00:00:00 +0000
1264@@ -1,16 +0,0 @@
1265-# ubuntuone.oauthdesktop - OAuth client support for desktop apps
1266-#
1267-# Copyright 2009 Canonical Ltd.
1268-#
1269-# This program is free software: you can redistribute it and/or modify it
1270-# under the terms of the GNU General Public License version 3, as published
1271-# by the Free Software Foundation.
1272-#
1273-# This program is distributed in the hope that it will be useful, but
1274-# WITHOUT ANY WARRANTY; without even the implied warranties of
1275-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1276-# PURPOSE. See the GNU General Public License for more details.
1277-#
1278-# You should have received a copy of the GNU General Public License along
1279-# with this program. If not, see <http://www.gnu.org/licenses/>.
1280-"""OAuth client authorisation code."""
1281
1282=== removed file 'ubuntuone/oauthdesktop/auth.py'
1283--- ubuntuone/oauthdesktop/auth.py 2010-02-10 17:35:26 +0000
1284+++ ubuntuone/oauthdesktop/auth.py 1970-01-01 00:00:00 +0000
1285@@ -1,480 +0,0 @@
1286-# ubuntuone.oauthdesktop.auth - Client authorization module
1287-#
1288-# Author: Stuart Langridge <stuart.langridge@canonical.com>
1289-#
1290-# Copyright 2009 Canonical Ltd.
1291-#
1292-# This program is free software: you can redistribute it and/or modify it
1293-# under the terms of the GNU General Public License version 3, as published
1294-# by the Free Software Foundation.
1295-#
1296-# This program is distributed in the hope that it will be useful, but
1297-# WITHOUT ANY WARRANTY; without even the implied warranties of
1298-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1299-# PURPOSE. See the GNU General Public License for more details.
1300-#
1301-# You should have received a copy of the GNU General Public License along
1302-# with this program. If not, see <http://www.gnu.org/licenses/>.
1303-"""OAuth client authorisation code.
1304-
1305-This code handles acquisition of an OAuth access token for a service,
1306-managed through the GNOME keyring for future use, and asynchronously.
1307-"""
1308-
1309-__metaclass__ = type
1310-
1311-import subprocess
1312-import random
1313-import dbus
1314-import os
1315-import socket, httplib, urllib
1316-
1317-import gnomekeyring
1318-from oauth import oauth
1319-try:
1320- from ubuntuone.clientdefs import VERSION
1321- ubuntuone_client_version = VERSION
1322-except ImportError:
1323- ubuntuone_client_version = "Unknown"
1324-from ubuntuone.oauthdesktop.key_acls import set_all_key_acls
1325-
1326-from threading import Thread
1327-from twisted.internet import reactor
1328-from twisted.web import server, resource
1329-
1330-from ubuntuone.oauthdesktop.logger import setupLogging
1331-logger = setupLogging("UbuntuOne.OAuthDesktop.auth")
1332-
1333-
1334-class NoAccessToken(Exception):
1335- """No access token available."""
1336-
1337-# NetworkManager State constants
1338-NM_STATE_UNKNOWN = 0
1339-NM_STATE_ASLEEP = 1
1340-NM_STATE_CONNECTING = 2
1341-NM_STATE_CONNECTED = 3
1342-NM_STATE_DISCONNECTED = 4
1343-
1344-# Monkeypatch httplib so that urllib will fail on invalid certificate
1345-# Only patch if we can import ssl to work around fail in 2.5
1346-try:
1347- import ssl
1348-except ImportError:
1349- pass
1350-else:
1351- def _connect_wrapper(self):
1352- """Override HTTPSConnection.connect to require certificate checks"""
1353- sock = socket.create_connection((self.host, self.port), self.timeout)
1354- try:
1355- if self._tunnel_host:
1356- self.sock = sock
1357- self._tunnel()
1358- except AttributeError:
1359- pass
1360- self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
1361- cert_reqs=ssl.CERT_REQUIRED,
1362- ca_certs="/etc/ssl/certs/ca-certificates.crt")
1363- httplib.HTTPSConnection.connect = _connect_wrapper
1364-
1365-
1366-class FancyURLOpenerWithRedirectedPOST(urllib.FancyURLopener):
1367- """FancyURLopener does not redirect postdata when redirecting POSTs"""
1368- version = "Ubuntu One/Login (%s)" % ubuntuone_client_version
1369- def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
1370- """Actually perform a redirect"""
1371- # All the same as the original, except passing data, below
1372- if 'location' in headers:
1373- newurl = headers['location']
1374- elif 'uri' in headers:
1375- newurl = headers['uri']
1376- else:
1377- return
1378- fp.read()
1379- fp.close()
1380- # In case the server sent a relative URL, join with original:
1381- newurl = urllib.basejoin(self.type + ":" + url, newurl)
1382-
1383- # pass data if present when we redirect
1384- if data:
1385- return self.open(newurl, data)
1386- else:
1387- return self.open(newurl)
1388-
1389-class AuthorisationClient(object):
1390- """OAuth authorisation client."""
1391- def __init__(self, realm, request_token_url, user_authorisation_url,
1392- access_token_url, consumer_key, consumer_secret,
1393- callback_parent, callback_denied=None,
1394- callback_notoken=None, callback_error=None, do_login=True,
1395- keyring=gnomekeyring):
1396- """Create an `AuthorisationClient` instance.
1397-
1398- @param realm: the OAuth realm.
1399- @param request_token_url: the OAuth request token URL.
1400- @param user_authorisation_url: the OAuth user authorisation URL.
1401- @param access_token_url: the OAuth access token URL.
1402- @param consumer_key: the OAuth consumer key.
1403- @param consumer_secret: the OAuth consumer secret.
1404- @param callback_parent: a function in the includer to call with a token
1405-
1406- The preceding parameters are defined in sections 3 and 4.1 of the
1407- OAuth Core 1.0 specification. The following parameters are not:
1408-
1409- @param callback_denied: a function to call if no token is available
1410- @param do_login: whether to create a token if one is not cached
1411- @param keychain: the keyring object to use (defaults to gnomekeyring)
1412-
1413- """
1414- self.realm = realm
1415- self.request_token_url = request_token_url
1416- self.user_authorisation_url = user_authorisation_url
1417- self.access_token_url = access_token_url
1418- self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
1419- self.callback_parent = callback_parent
1420- self.callback_denied = callback_denied
1421- self.callback_notoken = callback_notoken
1422- self.callback_error = callback_error
1423- self.do_login = do_login
1424- self.request_token = None
1425- self.saved_acquire_details = (None, None, None)
1426- self.keyring = keyring
1427- logger.debug("auth.AuthorisationClient created with parameters "+ \
1428- "realm='%s', request_token_url='%s', user_authorisation_url='%s',"+\
1429- "access_token_url='%s', consumer_key='%s', callback_parent='%s'",
1430- realm, request_token_url, user_authorisation_url, access_token_url,
1431- consumer_key, callback_parent)
1432-
1433- def _get_keyring_items(self):
1434- """Raw interface to obtain keyring items."""
1435- return self.keyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,
1436- {'ubuntuone-realm': self.realm,
1437- 'oauth-consumer-key':
1438- self.consumer.key})
1439-
1440- def _forward_error_callback(self, error):
1441- """Forward an error through callback_error()"""
1442- if self.callback_error:
1443- self.callback_error(str(error))
1444- else:
1445- raise error
1446-
1447- def get_access_token(self):
1448- """Get the access token from the keyring.
1449-
1450- If no token is available in the keyring, `NoAccessToken` is raised.
1451- """
1452- logger.debug("Trying to fetch the token from the keyring")
1453- try:
1454- items = self._get_keyring_items()
1455- except (gnomekeyring.NoMatchError,
1456- gnomekeyring.DeniedError):
1457- logger.debug("Access token was not in the keyring")
1458- raise NoAccessToken("No access token found.")
1459- logger.debug("Access token successfully found in the keyring")
1460- return oauth.OAuthToken.from_string(items[0].secret)
1461-
1462- def clear_token(self):
1463- """Clear any stored tokens from the keyring."""
1464- logger.debug("Searching keyring for existing tokens to delete.")
1465- try:
1466- items = self._get_keyring_items()
1467- except (gnomekeyring.NoMatchError,
1468- gnomekeyring.DeniedError):
1469- logger.debug("No preexisting tokens found")
1470- else:
1471- logger.debug("Deleting %s tokens from the keyring" % len(items))
1472- for item in items:
1473- try:
1474- self.keyring.item_delete_sync(None, item.item_id)
1475- except gnomekeyring.DeniedError:
1476- logger.debug("Permission denied deleting token")
1477-
1478- def store_token(self, access_token):
1479- """Store the given access token in the keyring.
1480-
1481- The keyring item is identified by the OAuth realm and consumer
1482- key to support multiple instances.
1483- """
1484- logger.debug("Trying to store the token in the keyring")
1485- try:
1486- item_id = self.keyring.item_create_sync(
1487- None,
1488- gnomekeyring.ITEM_GENERIC_SECRET,
1489- 'UbuntuOne token for %s' % self.realm,
1490- {'ubuntuone-realm': self.realm,
1491- 'oauth-consumer-key': self.consumer.key},
1492- access_token.to_string(),
1493- True)
1494- except gnomekeyring.DeniedError:
1495- logger.debug("Permission denied storing token")
1496- else:
1497- # set ACLs on the key for all apps listed in xdg BaseDir, but only
1498- # the root level one, not the user-level one
1499- logger.debug("Setting ACLs on the token in the keyring")
1500- set_all_key_acls(item_id=item_id)
1501-
1502- # keyring seems to take a while to actually apply the change
1503- # for when other people retrieve it, so sleep a bit.
1504- # this ought to get fixed.
1505- import time
1506- time.sleep(4)
1507-
1508- def have_access_token(self):
1509- """Returns true if an access token is available from the keyring."""
1510- try:
1511- self.get_access_token()
1512- except NoAccessToken:
1513- return False
1514- else:
1515- return True
1516-
1517- def make_token_request(self, oauth_request):
1518- """Perform the given `OAuthRequest` and return the associated token."""
1519-
1520- logger.debug("Making a token request")
1521- # Note that we monkeypatched httplib above to handle invalid certs
1522- # Ways this urlopen can fail:
1523- # bad certificate
1524- # raises IOError, e.args[1] == SSLError, e.args[1].errno == 1
1525- # No such server
1526- # raises IOError, e.args[1] == SSLError, e.args[1].errno == -2
1527- try:
1528- opener = FancyURLOpenerWithRedirectedPOST()
1529- fp = opener.open(oauth_request.http_url, oauth_request.to_postdata())
1530- data = fp.read()
1531- except IOError, e:
1532- self._forward_error_callback(e)
1533- return
1534-
1535- # we deliberately trap anything that might go wrong when parsing the
1536- # token, because we do not want this to explicitly fail
1537- # pylint: disable-msg=W0702
1538- try:
1539- out_token = oauth.OAuthToken.from_string(data)
1540- logger.debug("Token successfully requested")
1541- return out_token
1542- except:
1543- error = Exception(data)
1544- logger.error("Token was not successfully retrieved: data was '%s'",
1545- str(error))
1546- self._forward_error_callback(error)
1547-
1548- def open_in_browser(self, url):
1549- """Open the given URL in the user's web browser."""
1550- logger.debug("Opening '%s' in the browser", url)
1551- p = subprocess.Popen(["xdg-open", url], bufsize=4096,
1552- stderr=subprocess.PIPE)
1553- p.wait()
1554- if p.returncode != 0:
1555- errors = "".join(p.stderr.readlines())
1556- if errors != "":
1557- self._forward_error_callback(IOError(errors))
1558-
1559- def acquire_access_token_if_online(self, description=None, store=False):
1560- """Check to see if we are online before trying to acquire"""
1561- # Get NetworkManager state
1562- logger.debug("Checking whether we are online")
1563- try:
1564- nm = dbus.SystemBus().get_object('org.freedesktop.NetworkManager',
1565- '/org/freedesktop/NetworkManager',
1566- follow_name_owner_changes=True)
1567- except dbus.exceptions.DBusException:
1568- logger.warn("Unable to connect to NetworkManager. Trying anyway.")
1569- self.acquire_access_token(description, store)
1570- else:
1571- iface = dbus.Interface(nm, 'org.freedesktop.NetworkManager')
1572-
1573- def got_state(state):
1574- """Handler for when state() call succeeds."""
1575- if state == NM_STATE_CONNECTED:
1576- logger.debug("We are online")
1577- self.acquire_access_token(description, store)
1578- elif state == NM_STATE_CONNECTING:
1579- logger.debug("We are currently going online")
1580- # attach to NM's StateChanged signal
1581- signal_match = nm.connect_to_signal(
1582- signal_name="StateChanged",
1583- handler_function=self.connection_established,
1584- dbus_interface="org.freedesktop.NetworkManager")
1585- # stash the details so the handler_function can get at them
1586- self.saved_acquire_details = (signal_match, description,
1587- store)
1588- else:
1589- # NM is not connected: fail
1590- logger.debug("We are not online")
1591-
1592- def got_error(error):
1593- """Handler for D-Bus errors when calling state()."""
1594- if error.get_dbus_name() == \
1595- 'org.freedesktop.DBus.Error.ServiceUnknown':
1596- logger.debug("NetworkManager not available.")
1597- self.acquire_access_token(description, store)
1598- else:
1599- logger.error("Error contacting NetworkManager: %s" % \
1600- str(error))
1601-
1602-
1603- iface.state(reply_handler=got_state, error_handler=got_error)
1604-
1605- def connection_established(self, state):
1606- """NetworkManager's state has changed, and we're watching for
1607- a connection"""
1608- logger.debug("Online status has changed to %s" % state)
1609- if int(state) == NM_STATE_CONNECTED:
1610- signal_match, description, store = self.saved_acquire_details
1611- # disconnect the signal so we don't get called again
1612- signal_match.remove()
1613- # call the real acquire_access_token now it has a connection
1614- logger.debug("Correctly connected: now starting auth process")
1615- self.acquire_access_token(description, store)
1616- else:
1617- # connection changed but not to "connected", so keep waiting
1618- logger.debug("Not yet connected: continuing to wait")
1619-
1620- def acquire_access_token(self, description=None, store=False):
1621- """Create an OAuth access token authorised against the user."""
1622- signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
1623-
1624- # Create a request token ...
1625- logger.debug("Creating a request token to begin access request")
1626- parameters = {}
1627- if description:
1628- parameters['description'] = description
1629- # Add a nonce to the query so we know the callback (to our temp
1630- # webserver) came from us
1631- nonce = random.randint(1000000, 10000000)
1632-
1633- # start temporary webserver to receive browser response
1634- callback_url = self.get_temporary_httpd(nonce,
1635- self.retrieve_access_token, store)
1636-
1637- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
1638- callback=callback_url,
1639- http_url=self.request_token_url,
1640- oauth_consumer=self.consumer,
1641- parameters=parameters)
1642- oauth_request.sign_request(signature_method, self.consumer, None)
1643- logger.debug("Making token request")
1644- self.request_token = self.make_token_request(oauth_request)
1645-
1646- # Request authorisation from the user
1647- oauth_request = oauth.OAuthRequest.from_token_and_callback(
1648- http_url=self.user_authorisation_url,
1649- token=self.request_token)
1650- nodename = os.uname()[1]
1651- if nodename:
1652- oauth_request.set_parameter("description", nodename)
1653- Thread(target=self.open_in_browser, name="authorization",
1654- args=(oauth_request.to_url(),)).start()
1655-
1656- def get_temporary_httpd(self, nonce, retrieve_function, store):
1657- "A separate class so it can be mocked in testing"
1658- logger.debug("Creating a listening temp web server")
1659- site = TemporaryTwistedWebServer(nonce=nonce,
1660- retrieve_function=retrieve_function, store_yes_no=store)
1661- temphttpd = server.Site(site)
1662- temphttpdport = reactor.listenTCP(0, temphttpd)
1663- callback_url = "http://localhost:%s/?nonce=%s" % (
1664- temphttpdport.getHost().port, nonce)
1665- site.set_port(temphttpdport)
1666- logger.debug("Webserver listening on port '%s'", temphttpdport)
1667- return callback_url
1668-
1669- def retrieve_access_token(self, store=False, verifier=None):
1670- """Retrieve the access token, once OAuth is done. This is a callback."""
1671- logger.debug("Access token callback from temp webserver")
1672- signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
1673- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
1674- http_url=self.access_token_url,
1675- oauth_consumer=self.consumer,
1676- token=self.request_token)
1677- oauth_request.set_parameter("oauth_verifier", verifier)
1678- oauth_request.sign_request(
1679- signature_method, self.consumer, self.request_token)
1680- logger.debug("Retrieving access token from OAuth")
1681- access_token = self.make_token_request(oauth_request)
1682- if not access_token:
1683- logger.error("Failed to get access token.")
1684- if self.callback_denied is not None:
1685- self.callback_denied()
1686- else:
1687- if store:
1688- logger.debug("Storing access token in keyring")
1689- self.store_token(access_token)
1690- logger.debug("Calling the callback_parent")
1691- self.callback_parent(access_token)
1692-
1693- def ensure_access_token(self, description=None):
1694- """Returns an access token, either from the keyring or newly acquired.
1695-
1696- If a new token is acquired, it will be stored in the keyring
1697- for future use.
1698- """
1699- try:
1700- access_token = self.get_access_token()
1701- self.callback_parent(access_token)
1702- except NoAccessToken:
1703- if self.do_login:
1704- access_token = self.acquire_access_token_if_online(
1705- description,
1706- store=True)
1707- else:
1708- if self.callback_notoken is not None:
1709- self.callback_notoken()
1710-
1711-
1712-class TemporaryTwistedWebServer(resource.Resource):
1713- """A temporary httpd for the oauth process to call back to"""
1714- isLeaf = True
1715- def __init__(self, nonce, store_yes_no, retrieve_function):
1716- """Initialize the temporary web server."""
1717- resource.Resource.__init__(self)
1718- self.nonce = nonce
1719- self.store_yes_no = store_yes_no
1720- self.retrieve_function = retrieve_function
1721- reactor.callLater(600, self.stop) # ten minutes
1722- self.port = None
1723- def set_port(self, port):
1724- """Save the Twisted port object so we can stop it later"""
1725- self.port = port
1726- def stop(self):
1727- """Stop the httpd"""
1728- logger.debug("Stopping temp webserver")
1729- self.port.stopListening()
1730- def render_GET(self, request):
1731- """Handle incoming web requests"""
1732- logger.debug("Incoming temp webserver hit received")
1733- nonce = request.args.get("nonce", [None])[0]
1734- url = request.args.get("return", ["https://one.ubuntu.com/"])[0]
1735- verifier = request.args.get("oauth_verifier", [None])[0]
1736- logger.debug("Got verifier %s" % verifier)
1737- if nonce and (str(nonce) == str(self.nonce) and verifier):
1738- self.retrieve_function(store=self.store_yes_no, verifier=verifier)
1739- reactor.callLater(3, self.stop)
1740- return """<!doctype html>
1741- <html><head><meta http-equiv="refresh"
1742- content="0;url=%(url)s">
1743- </head>
1744- <body>
1745- <p>You should now automatically <a
1746- href="%(url)s">return to %(url)s</a>.</p>
1747- </body>
1748- </html>
1749- """ % { 'url' : url }
1750- else:
1751- self.retrieve_function(store=self.store_yes_no, verifier=verifier)
1752- reactor.callLater(3, self.stop)
1753- request.setResponseCode(400)
1754- return """<!doctype html>
1755- <html><head><title>Error</title></head>
1756- <body>
1757- <h1>There was an error</h1>
1758- <p>The authentication process has not succeeded. This may be a
1759- temporary problem; please try again in a few minutes.</p>
1760- </body>
1761- </html>
1762- """
1763-
1764-
1765-
1766
1767=== removed file 'ubuntuone/oauthdesktop/config.py'
1768--- ubuntuone/oauthdesktop/config.py 2009-07-22 18:14:52 +0000
1769+++ ubuntuone/oauthdesktop/config.py 1970-01-01 00:00:00 +0000
1770@@ -1,47 +0,0 @@
1771-# ubuntuone.oauthdesktop.config - Configuration for OAuthDesktop
1772-#
1773-# Author: Stuart Langridge <stuart.langridge@canonical.com>
1774-#
1775-# Copyright 2009 Canonical Ltd.
1776-#
1777-# This program is free software: you can redistribute it and/or modify it
1778-# under the terms of the GNU General Public License version 3, as published
1779-# by the Free Software Foundation.
1780-#
1781-# This program is distributed in the hope that it will be useful, but
1782-# WITHOUT ANY WARRANTY; without even the implied warranties of
1783-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1784-# PURPOSE. See the GNU General Public License for more details.
1785-#
1786-# You should have received a copy of the GNU General Public License along
1787-# with this program. If not, see <http://www.gnu.org/licenses/>.
1788-"Find the config file for ubuntuone-oauth-config"
1789-import os, ConfigParser
1790-from xdg.BaseDirectory import load_first_config
1791-
1792-def get_config(use_tmpconfig=True):
1793- """Return a ConfigParser object from a config file.
1794- Config file is looked for in the source tree first and then in the
1795- FreeDesktop BaseDirectory folders.
1796- """
1797- # if tmpconfig exists, then we are running out of the source tree
1798- tmpconfig = os.path.realpath(os.path.join(
1799- __file__, "../../../data/oauth_urls"
1800- ))
1801- if os.path.isfile(tmpconfig) and use_tmpconfig:
1802- config_file = tmpconfig
1803- else:
1804- config_file = load_first_config('ubuntuone','oauth_urls')
1805-
1806- cfp = ConfigParser.ConfigParser()
1807- cfp.FILENAME = None
1808- if config_file is not None:
1809- try:
1810- cfp.read(config_file)
1811- cfp.FILENAME = config_file
1812- except ConfigParser.Error:
1813- cfp = ConfigParser.ConfigParser()
1814- cfp.FILENAME = None
1815- return cfp
1816-
1817-
1818
1819=== removed file 'ubuntuone/oauthdesktop/key_acls.py'
1820--- ubuntuone/oauthdesktop/key_acls.py 2010-03-10 21:11:45 +0000
1821+++ ubuntuone/oauthdesktop/key_acls.py 1970-01-01 00:00:00 +0000
1822@@ -1,162 +0,0 @@
1823-# ubuntuone.oauthdesktop.key_acls - OAuth ACL handling for keyring
1824-#
1825-# Author: Stuart Langridge <stuart.langridge@canonical.com>
1826-#
1827-# Copyright 2009 Canonical Ltd.
1828-#
1829-# This program is free software: you can redistribute it and/or modify it
1830-# under the terms of the GNU General Public License version 3, as published
1831-# by the Free Software Foundation.
1832-#
1833-# This program is distributed in the hope that it will be useful, but
1834-# WITHOUT ANY WARRANTY; without even the implied warranties of
1835-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1836-# PURPOSE. See the GNU General Public License for more details.
1837-#
1838-# You should have received a copy of the GNU General Public License along
1839-# with this program. If not, see <http://www.gnu.org/licenses/>.
1840-"""OAuth client authorisation code.
1841-
1842-This code finds all apps that have pre-registered themselves as wanting to
1843-access OAuth tokens from the Gnome keyring without the user having to approve
1844-that, and sets ACLs on relevant keys so they can do so.
1845-
1846-Apps pre-register themselves by dropping an ini file in
1847-/etc/xdg/ubuntuone/oauth_registration.d/ in which each section has keys
1848-realm, consumer_key, exe_path, application_name.
1849-"""
1850-
1851-import xdg.BaseDirectory, os, ConfigParser, gnomekeyring
1852-
1853-def get_privileged_config_folder(use_source_tree_folder=True):
1854- """Find the XDG config folder to use which is not the user's personal
1855- config (i.e., ~/.config) so that files in it are root-owned"""
1856-
1857- # First, check for folder (if we're running from the source tree)
1858- if use_source_tree_folder:
1859- source_tree_folder = os.path.join(
1860- os.path.split(__file__)[0],
1861- "../../data/oauth_registration.d")
1862- if os.path.isdir(source_tree_folder):
1863- return os.path.join(source_tree_folder, "..")
1864-
1865- # Otherwise, check for proper XDG folders
1866- privileged_folders = [x for x in
1867- xdg.BaseDirectory.load_config_paths('ubuntuone')
1868- if not x.startswith(xdg.BaseDirectory.xdg_config_home)]
1869- if privileged_folders:
1870- return privileged_folders[0]
1871- else:
1872- return None
1873-
1874-def get_acl_preregistrations(use_source_tree_folder=True):
1875- "Return a list of all config files in the pre-registration folder"
1876- config_folder = get_privileged_config_folder(use_source_tree_folder)
1877- if config_folder:
1878- conf_dir = os.path.join(config_folder,
1879- "oauth_registration.d")
1880- if os.path.isdir(conf_dir):
1881- return [os.path.join(conf_dir, x) for x in os.listdir(conf_dir)]
1882- return []
1883-
1884-def get_item_ids_for_realm(realm, consumer_key):
1885- "Find all keyring tokens for a specific realm/consumer_key"
1886- if realm == "http://localhost":
1887- # if realm (from the config file) is localhost, then the token
1888- # will have ubuntuone-realm == http://localhost:SOMETHING
1889- # so find all keys with this consumer_key, and pass on any
1890- # where ubuntuone-realm begins with http://localhost:
1891- try:
1892- items = gnomekeyring.find_items_sync(
1893- gnomekeyring.ITEM_GENERIC_SECRET,
1894- {
1895- 'oauth-consumer-key': consumer_key})
1896- except (gnomekeyring.NoMatchError,
1897- gnomekeyring.DeniedError):
1898- return []
1899- items = [x.item_id for x in items if
1900- x.attributes.get("ubuntuone-realm", "").startswith("http://localhost:")]
1901- return items
1902- else:
1903- # realm was not localhost, so search for it explicitly
1904- try:
1905- items = gnomekeyring.find_items_sync(
1906- gnomekeyring.ITEM_GENERIC_SECRET,
1907- {'ubuntuone-realm': realm,
1908- 'oauth-consumer-key': consumer_key})
1909- except (gnomekeyring.NoMatchError,
1910- gnomekeyring.DeniedError):
1911- return []
1912- return [x.item_id for x in items]
1913-
1914-def set_single_acl(app_sets, specific_item_id=None):
1915- """Allow a specified set of apps to access a matching keyring
1916- token without prompts"""
1917- for realm, consumer_key, exe_path, application_name in app_sets:
1918- if specific_item_id is None:
1919- items = get_item_ids_for_realm(realm, consumer_key)
1920- else:
1921- # item_id specified
1922- items = [specific_item_id]
1923-
1924- # set an ACL on the key so the calling app can read it without
1925- # a prompt dialog
1926- for item_id in items:
1927- acl = gnomekeyring.item_get_acl_sync(None, item_id)
1928- new_acls = False
1929- real_exe_path = os.path.realpath(exe_path)
1930- for acl_item in acl:
1931- if acl_item.get_display_name() == application_name and \
1932- acl_item.get_path_name() == real_exe_path:
1933- # this ACL is already set
1934- break
1935- else:
1936- appref = gnomekeyring.ApplicationRef()
1937- ac = gnomekeyring.AccessControl(appref,
1938- gnomekeyring.ACCESS_READ |
1939- gnomekeyring.ACCESS_WRITE | gnomekeyring.ACCESS_REMOVE)
1940- ac.set_display_name(application_name)
1941- ac.set_path_name(real_exe_path)
1942- acl.append(ac)
1943- new_acls = True
1944- if new_acls:
1945- gnomekeyring.item_set_acl_sync(None, item_id, acl)
1946-
1947-def set_all_key_acls(item_id=None, use_source_tree_folder=True):
1948- """For each file in the config folder, get the (realm, key) pair that
1949- the program therein is interested in and register the program as able
1950- to access those keys by setting an ACL on them."""
1951- for config_file in get_acl_preregistrations(use_source_tree_folder):
1952- cfp = ConfigParser.ConfigParser()
1953- try:
1954- cfp.read(config_file)
1955- except ConfigParser.Error:
1956- continue
1957-
1958- app_sets = []
1959-
1960- for section in cfp.sections():
1961- try:
1962- realm = cfp.get(section, "realm")
1963- except ConfigParser.NoOptionError:
1964- realm = None
1965- try:
1966- consumer_key = cfp.get(section, "consumer_key")
1967- except ConfigParser.NoOptionError:
1968- consumer_key = None
1969- try:
1970- exe_path = cfp.get(section, "exe_path")
1971- except ConfigParser.NoOptionError:
1972- exe_path = None
1973- try:
1974- application_name = cfp.get(section, "application_name")
1975- except ConfigParser.NoOptionError:
1976- application_name = None
1977- if realm and consumer_key and exe_path and application_name:
1978- app_sets.append((realm, consumer_key, exe_path,
1979- application_name))
1980-
1981- if app_sets:
1982- set_single_acl(app_sets, specific_item_id=item_id)
1983-
1984-
1985
1986=== removed file 'ubuntuone/oauthdesktop/logger.py'
1987--- ubuntuone/oauthdesktop/logger.py 2009-10-16 19:56:57 +0000
1988+++ ubuntuone/oauthdesktop/logger.py 1970-01-01 00:00:00 +0000
1989@@ -1,49 +0,0 @@
1990-# ubuntuone.oauthdesktop.logger - logging miscellany
1991-#
1992-# Author: Stuart Langridge <stuart.langridge@canonical.com>
1993-#
1994-# Copyright 2009 Canonical Ltd.
1995-#
1996-# This program is free software: you can redistribute it and/or modify it
1997-# under the terms of the GNU General Public License version 3, as published
1998-# by the Free Software Foundation.
1999-#
2000-# This program is distributed in the hope that it will be useful, but
2001-# WITHOUT ANY WARRANTY; without even the implied warranties of
2002-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2003-# PURPOSE. See the GNU General Public License for more details.
2004-#
2005-# You should have received a copy of the GNU General Public License along
2006-# with this program. If not, see <http://www.gnu.org/licenses/>.
2007-"""Miscellaneous logging functions."""
2008-import os
2009-import logging
2010-import xdg.BaseDirectory
2011-
2012-from logging.handlers import RotatingFileHandler
2013-
2014-home = xdg.BaseDirectory.xdg_cache_home
2015-LOGFOLDER = os.path.join(home, 'ubuntuone', 'log')
2016-# create log folder if it doesn't exists
2017-if not os.path.exists(LOGFOLDER):
2018- os.makedirs(LOGFOLDER)
2019-
2020-LOGFILENAME = os.path.join(LOGFOLDER, 'oauth-login.log')
2021-
2022-# Only log this level and above
2023-LOG_LEVEL = logging.INFO
2024-
2025-root_formatter = logging.Formatter(
2026- fmt="%(asctime)s:%(msecs)s %(name)s %(message)s")
2027-root_handler = RotatingFileHandler(LOGFILENAME, maxBytes=1048576,
2028- backupCount=1)
2029-root_handler.setLevel(LOG_LEVEL)
2030-root_handler.setFormatter(root_formatter)
2031-
2032-def setupLogging(log_domain):
2033- """Create basic logger to set filename"""
2034- logger = logging.getLogger(log_domain)
2035- logger.propagate = False
2036- logger.setLevel(LOG_LEVEL)
2037- logger.addHandler(root_handler)
2038- return logger
2039
2040=== removed file 'ubuntuone/oauthdesktop/main.py'
2041--- ubuntuone/oauthdesktop/main.py 2010-03-30 21:11:52 +0000
2042+++ ubuntuone/oauthdesktop/main.py 1970-01-01 00:00:00 +0000
2043@@ -1,296 +0,0 @@
2044-#!/usr/bin/python
2045-
2046-# ubuntuone.oauthdesktop.main - main login handling interface
2047-#
2048-# Author: Stuart Langridge <stuart.langridge@canonical.com>
2049-#
2050-# Copyright 2009 Canonical Ltd.
2051-#
2052-# This program is free software: you can redistribute it and/or modify it
2053-# under the terms of the GNU General Public License version 3, as published
2054-# by the Free Software Foundation.
2055-#
2056-# This program is distributed in the hope that it will be useful, but
2057-# WITHOUT ANY WARRANTY; without even the implied warranties of
2058-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2059-# PURPOSE. See the GNU General Public License for more details.
2060-#
2061-# You should have received a copy of the GNU General Public License along
2062-# with this program. If not, see <http://www.gnu.org/licenses/>.
2063-"""OAuth login handler.
2064-
2065-A command-line utility which accepts requests for OAuth login over D-Bus,
2066-handles the OAuth process (including adding the OAuth access token to the
2067-gnome keyring), and then alerts the calling app (and others) with a D-Bus
2068-signal so they can retrieve the new token.
2069-"""
2070-
2071-import dbus.service, urlparse, time, gobject
2072-import pynotify
2073-
2074-from dbus.mainloop.glib import DBusGMainLoop
2075-
2076-from ubuntuone.oauthdesktop.config import get_config
2077-
2078-from ubuntuone.oauthdesktop.logger import setupLogging
2079-logger = setupLogging("UbuntuOne.OAuthDesktop.main")
2080-
2081-DBusGMainLoop(set_as_default=True)
2082-
2083-# Disable the invalid name warning, as we have a lot of DBus style names
2084-# pylint: disable-msg=C0103
2085-
2086-class NoDefaultConfigError(Exception):
2087- """No default section in configuration file"""
2088- pass
2089-
2090-class BadRealmError(Exception):
2091- """Realm must be a URL"""
2092- pass
2093-
2094-class LoginProcessor:
2095- """Actually do the work of processing passed parameters"""
2096- def __init__(self, dbus_object, use_libnotify=True):
2097- """Initialize the login processor."""
2098- logger.debug("Creating a LoginProcessor")
2099- self.use_libnotify = use_libnotify
2100- if self.use_libnotify and pynotify:
2101- logger.debug("Hooking libnotify")
2102- pynotify.init("UbuntuOne Login")
2103- self.note1 = None
2104- self.realm = None
2105- self.consumer_key = None
2106- self.dbus_object = dbus_object
2107- logger.debug("Getting configuration")
2108- self.config = get_config()
2109-
2110- def login(self, realm, consumer_key, do_login=True):
2111- """Initiate an OAuth login"""
2112- logger.debug("Initiating OAuth login in LoginProcessor")
2113- self.realm = str(realm) # because they are dbus.Strings, not str
2114- self.consumer_key = str(consumer_key)
2115-
2116- logger.debug("Obtaining OAuth urls")
2117- (request_token_url, user_authorisation_url,
2118- access_token_url, consumer_secret) = self.get_config_urls(realm)
2119- logger.debug("OAuth URLs are: request='%s', userauth='%s', " +\
2120- "access='%s', secret='%s'", request_token_url,
2121- user_authorisation_url, access_token_url, consumer_secret)
2122-
2123- from ubuntuone.oauthdesktop.auth import AuthorisationClient
2124- client = AuthorisationClient(self.realm,
2125- request_token_url,
2126- user_authorisation_url,
2127- access_token_url, self.consumer_key,
2128- consumer_secret,
2129- callback_parent=self.got_token,
2130- callback_denied=self.got_denial,
2131- callback_notoken=self.got_no_token,
2132- callback_error=self.got_error,
2133- do_login=do_login)
2134-
2135- logger.debug("Calling auth.client.ensure_access_token in thread")
2136- gobject.timeout_add_seconds(1, client.ensure_access_token)
2137-
2138- def clear_token(self, realm, consumer_key):
2139- """Remove the currently stored OAuth token from the keyring."""
2140- self.realm = str(realm)
2141- self.consumer_key = str(consumer_key)
2142- (request_token_url, user_authorisation_url,
2143- access_token_url, consumer_secret) = self.get_config_urls(self.realm)
2144- from ubuntuone.oauthdesktop.auth import AuthorisationClient
2145- client = AuthorisationClient(self.realm,
2146- request_token_url,
2147- user_authorisation_url,
2148- access_token_url,
2149- self.consumer_key, consumer_secret,
2150- callback_parent=self.got_token,
2151- callback_denied=self.got_denial,
2152- callback_notoken=self.got_no_token,
2153- callback_error=self.got_error)
2154- gobject.timeout_add_seconds(1, client.clear_token)
2155-
2156- def error_handler(self, failure):
2157- """Deal with errors returned from auth process"""
2158- logger.debug("Error returned from auth process")
2159- self.dbus_object.currently_authing = False # not block future requests
2160-
2161- def get_config_urls(self, realm):
2162- """Look up the URLs to use in the config file"""
2163- logger.debug("Fetching config URLs for realm='%s'", realm)
2164- if self.config.has_section(realm):
2165- logger.debug("Realm '%s' is in config", realm)
2166- request_token_url = self.__get_url(realm, "request_token_url")
2167- user_authorisation_url = self.__get_url(realm,
2168- "user_authorisation_url")
2169- access_token_url = self.__get_url(realm, "access_token_url")
2170- consumer_secret = self.__get_option(realm, "consumer_secret")
2171- elif realm.startswith("http://localhost") and \
2172- self.config.has_section("http://localhost"):
2173- logger.debug("Realm is localhost and is in config")
2174- request_token_url = self.__get_url("http://localhost",
2175- "request_token_url", realm)
2176- user_authorisation_url = self.__get_url("http://localhost",
2177- "user_authorisation_url", realm)
2178- access_token_url = self.__get_url("http://localhost",
2179- "access_token_url", realm)
2180- consumer_secret = self.__get_option("http://localhost",
2181- "consumer_secret")
2182- elif self.is_valid_url(realm):
2183- logger.debug("Realm '%s' is not in config", realm)
2184- request_token_url = self.__get_url("default",
2185- "request_token_url", realm)
2186- user_authorisation_url = self.__get_url("default",
2187- "user_authorisation_url", realm)
2188- access_token_url = self.__get_url("default",
2189- "access_token_url", realm)
2190- consumer_secret = self.__get_option(realm, "consumer_secret")
2191- else:
2192- logger.debug("Realm '%s' is a bad realm", realm)
2193- raise BadRealmError
2194- return (request_token_url, user_authorisation_url,
2195- access_token_url, consumer_secret)
2196-
2197- def is_valid_url(self, url):
2198- """Simple check for URL validity"""
2199- # pylint: disable-msg=W0612
2200- scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
2201- if scheme and netloc:
2202- return True
2203- else:
2204- return False
2205-
2206- def got_token(self, access_token):
2207- """Callback function when access token has been retrieved"""
2208- logger.debug("Token retrieved, calling NewCredentials function")
2209- self.dbus_object.NewCredentials(self.realm, self.consumer_key)
2210-
2211- def got_denial(self):
2212- """Callback function when request token has been denied"""
2213- self.dbus_object.AuthorizationDenied()
2214-
2215- def got_no_token(self):
2216- """Callback function when access token is not in keyring."""
2217- self.dbus_object.NoCredentials()
2218-
2219- def got_error(self, message):
2220- """Callback function to emit an error message over DBus."""
2221- self.dbus_object.OAuthError(message)
2222-
2223- def __get_url(self, realm, option, actual_realm=None):
2224- """Construct a full URL from realm and a URLpath for that realm in
2225- the config file."""
2226- if actual_realm:
2227- realm_to_use = actual_realm
2228- else:
2229- realm_to_use = realm
2230- urlstub = self.__get_option(realm, option)
2231- return urlparse.urljoin(realm_to_use, urlstub)
2232-
2233- def __get_option(self, realm, option):
2234- """Return a specific option for that realm in
2235- the config file. If the realm does not exist in the config file,
2236- fall back to the [default] section."""
2237- if self.config.has_section(realm) and \
2238- self.config.has_option(realm, option):
2239- urlstub = self.config.get(realm, option)
2240- return urlstub
2241-
2242- # either the realm exists and this url does not, or
2243- # the realm doesn't exist; either way, fall back to [default] section
2244- urlstub = self.config.get("default", option, None)
2245- if urlstub is not None:
2246- return urlstub
2247-
2248- # this url does not exist in default section either
2249- # this shouldn't happen
2250- raise NoDefaultConfigError("No default configuration for %s" % option)
2251-
2252-
2253-class Login(dbus.service.Object):
2254- """Object which listens for D-Bus OAuth requests"""
2255- def __init__(self, bus_name):
2256- """Initiate the Login object."""
2257- dbus.service.Object.__init__(self, object_path="/", bus_name=bus_name)
2258- self.processor = LoginProcessor(self)
2259- self.currently_authing = False
2260- logger.debug("Login D-Bus service starting up")
2261-
2262- @dbus.service.method(dbus_interface='com.ubuntuone.Authentication',
2263- in_signature='ss', out_signature='')
2264- def login(self, realm, consumer_key):
2265- """D-Bus method, exported over the bus, to initiate an OAuth login"""
2266- logger.debug("login() D-Bus message received with realm='%s', " +
2267- "consumer_key='%s'", realm, consumer_key)
2268- if self.currently_authing:
2269- logger.debug("Currently in the middle of OAuth: rejecting this")
2270- return
2271- self.currently_authing = True
2272- self.processor.login(realm, consumer_key)
2273-
2274- @dbus.service.method(dbus_interface='com.ubuntuone.Authentication',
2275- in_signature='ssb', out_signature='')
2276- def maybe_login(self, realm, consumer_key, do_login):
2277- """
2278- D-Bus method, exported over the bus, to maybe initiate an OAuth login
2279- """
2280- logger.debug("maybe_login() D-Bus message received with realm='%s', " +
2281- "consumer_key='%s'", realm, consumer_key)
2282- if self.currently_authing:
2283- logger.debug("Currently in the middle of OAuth: rejecting this")
2284- return
2285- self.currently_authing = True
2286- self.processor.login(realm, consumer_key, do_login)
2287-
2288- @dbus.service.method(dbus_interface='com.ubuntuone.Authentication',
2289- in_signature='ss', out_signature='')
2290- def clear_token(self, realm, consumer_key):
2291- """
2292- D-Bus method, exported over the bus, to clear the existing token.
2293- """
2294- self.processor.clear_token(realm, consumer_key)
2295-
2296- @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication',
2297- signature='ss')
2298- def NewCredentials(self, realm, consumer_key):
2299- """Fire D-Bus signal when the user accepts authorization."""
2300- logger.debug("Firing the NewCredentials signal")
2301- self.currently_authing = False
2302- return (self.processor.realm, self.processor.consumer_key)
2303-
2304- @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication')
2305- def AuthorizationDenied(self):
2306- """Fire the signal when the user denies authorization."""
2307- self.currently_authing = False
2308-
2309- @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication')
2310- def NoCredentials(self):
2311- """Fired when the user does not have a token in the keyring."""
2312- self.currently_authing = False
2313-
2314- @dbus.service.signal(dbus_interface='com.ubuntuone.Authentication',
2315- signature='s')
2316- def OAuthError(self, message):
2317- """Fire the signal when an error needs to be propagated to the user."""
2318- self.currently_authing = False
2319- return message
2320-
2321-def main():
2322- """Start everything"""
2323- logger.debug("Starting up at %s", time.asctime())
2324- logger.debug("Installing the Twisted glib2reactor")
2325- from twisted.internet import glib2reactor # for non-GUI apps
2326- glib2reactor.install()
2327- from twisted.internet import reactor
2328-
2329- logger.debug("Creating the D-Bus service")
2330- Login(dbus.service.BusName("com.ubuntuone.Authentication",
2331- bus=dbus.SessionBus()))
2332- # cleverness here to say:
2333- # am I already running (bound to this d-bus name)?
2334- # if so, send a signal to the already running instance
2335- # this means that this app can be started from an x-ubutnuone: URL
2336- # to kick off the signin process
2337- logger.debug("Starting the reactor mainloop")
2338- reactor.run()
2339-
2340
2341=== modified file 'ubuntuone/syncdaemon/dbus_interface.py'
2342--- ubuntuone/syncdaemon/dbus_interface.py 2010-06-02 18:00:29 +0000
2343+++ ubuntuone/syncdaemon/dbus_interface.py 2010-06-16 20:35:34 +0000
2344@@ -33,7 +33,7 @@
2345 from ubuntuone.syncdaemon.interfaces import IMarker
2346 from ubuntuone.syncdaemon import config
2347 from ubuntuone.syncdaemon.volume_manager import Share, UDF, VolumeDoesNotExist
2348-
2349+from ubuntu_sso import DBUS_IFACE_AUTH_NAME, DBUS_PATH_AUTH
2350
2351 # Disable the "Invalid Name" check here, as we have lots of DBus style names
2352 # pylint: disable-msg=C0103
2353@@ -58,10 +58,6 @@
2354 NM_STATE_EVENTS = {NM_STATE_CONNECTED: 'SYS_NET_CONNECTED',
2355 NM_STATE_DISCONNECTED: 'SYS_NET_DISCONNECTED'}
2356
2357-# OAuthDesktop constants
2358-DBUS_IFACE_AUTH_NAME = "com.ubuntuone.Authentication"
2359-DBUS_PATH_AUTH = "/"
2360-
2361 logger = logging.getLogger("ubuntuone.SyncDaemon.DBus")
2362
2363 def get_classname(thing):

Subscribers

People subscribed via source and target branches