Merge lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-0.99.6 into lp:ubuntu/maverick/ubuntu-sso-client

Proposed by Natalia Bidart
Status: Merged
Merged at revision: 12
Proposed branch: lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-0.99.6
Merge into: lp:ubuntu/maverick/ubuntu-sso-client
Diff against target: 5327 lines (+1281/-2427)
37 files modified
MANIFEST.in (+0/-8)
PKG-INFO (+1/-4)
bin/show_nm_state (+0/-41)
bin/ubuntu-sso-login (+39/-188)
bin/ubuntu-sso-login-gui (+0/-53)
contrib/dbus_util.py (+5/-1)
contrib/test (+6/-6)
contrib/testing/__init__.py (+1/-1)
contrib/testing/testcase.py (+21/-157)
data/oauth_registration.d/ubuntuone (+0/-23)
data/oauth_urls (+0/-5)
debian/changelog (+55/-0)
debian/postinst (+25/-0)
debian/postrm (+15/-0)
debian/preinst (+14/-0)
pylintrc (+309/-0)
run-tests (+2/-5)
setup.py (+11/-14)
ubuntu_sso/__init__.py (+3/-3)
ubuntu_sso/auth.py (+4/-464)
ubuntu_sso/config.py (+0/-47)
ubuntu_sso/gui.py (+107/-45)
ubuntu_sso/key_acls.py (+0/-157)
ubuntu_sso/keyring.py (+37/-6)
ubuntu_sso/logger.py (+18/-16)
ubuntu_sso/main.py (+79/-287)
ubuntu_sso/networkstate.py (+6/-6)
ubuntu_sso/tests/bin/show_gui (+57/-0)
ubuntu_sso/tests/bin/show_nm_state (+41/-0)
ubuntu_sso/tests/test_auth.py (+0/-261)
ubuntu_sso/tests/test_config.py (+0/-85)
ubuntu_sso/tests/test_gui.py (+202/-33)
ubuntu_sso/tests/test_key_acls.py (+0/-166)
ubuntu_sso/tests/test_keyring.py (+38/-14)
ubuntu_sso/tests/test_login.py (+0/-188)
ubuntu_sso/tests/test_main.py (+145/-119)
ubuntu_sso/tests/test_networkstate.py (+40/-24)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-0.99.6
Reviewer Review Type Date Requested Status
Ken VanDine Approve
Review via email: mp+34920@code.launchpad.net

Description of the change

Bug fixes included on upstream versions 0.99.5 and 0.99.6.

To post a comment you must log in.
14. By Natalia Bidart

Fixing typos on pre/postinst.

Revision history for this message
Ken VanDine (ken-vandine) wrote :

Approve!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file 'MANIFEST.in'
2--- MANIFEST.in 2010-08-26 01:07:41 +0000
3+++ MANIFEST.in 1970-01-01 00:00:00 +0000
4@@ -1,8 +0,0 @@
5-include MANIFEST.in
6-include COPYING README
7-include run-tests
8-recursive-include po *
9-recursive-include bin *
10-recursive-include data *
11-recursive-include ubuntu_sso *.py
12-recursive-include contrib *.py *.conf test
13
14=== modified file 'PKG-INFO'
15--- PKG-INFO 2010-08-27 22:16:40 +0000
16+++ PKG-INFO 2010-09-09 14:47:44 +0000
17@@ -1,6 +1,6 @@
18 Metadata-Version: 1.1
19 Name: ubuntu-sso-client
20-Version: 0.99.4
21+Version: 0.99.6
22 Summary: Ubuntu Single Sign-On client
23 Home-page: https://launchpad.net/ubuntu-sso-client
24 Author: Natalia Bidart
25@@ -15,12 +15,9 @@
26 Requires: gtk
27 Requires: mocker
28 Requires: oauth
29-Requires: testresources
30 Requires: twisted.internet
31 Requires: twisted.python
32 Requires: twisted.trial.unittest
33-Requires: twisted.web
34 Requires: webkit
35 Requires: xdg
36-Requires: zope.interface
37 Provides: ubuntu_sso
38
39=== removed file 'bin/show_nm_state'
40--- bin/show_nm_state 2010-08-24 15:47:58 +0000
41+++ bin/show_nm_state 1970-01-01 00:00:00 +0000
42@@ -1,41 +0,0 @@
43-#!/usr/bin/python
44-
45-# show_nm_state - Show the state of the NetworkManager daemon
46-#
47-# Author: Alejandro J. Cura <alecu@canonical.com>
48-#
49-# Copyright 2010 Canonical Ltd.
50-#
51-# This program is free software: you can redistribute it and/or modify it
52-# under the terms of the GNU General Public License version 3, as published
53-# by the Free Software Foundation.
54-#
55-# This program is distributed in the hope that it will be useful, but
56-# WITHOUT ANY WARRANTY; without even the implied warranties of
57-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
58-# PURPOSE. See the GNU General Public License for more details.
59-#
60-# You should have received a copy of the GNU General Public License along
61-# with this program. If not, see <http://www.gnu.org/licenses/>.
62-"""A test program that just shows the network state."""
63-
64-import gobject
65-
66-from dbus.mainloop.glib import DBusGMainLoop
67-
68-from ubuntu_sso.networkstate import NetworkManagerState, nm_state_names
69-
70-DBusGMainLoop(set_as_default=True)
71-loop = gobject.MainLoop()
72-
73-
74-def got_state(state):
75- """Called when the network state is found."""
76- try:
77- print nm_state_names[state]
78- finally:
79- loop.quit()
80-
81-nms = NetworkManagerState(got_state)
82-nms.find_online_state()
83-loop.run()
84
85=== modified file 'bin/ubuntu-sso-login'
86--- bin/ubuntu-sso-login 2010-08-24 15:47:58 +0000
87+++ bin/ubuntu-sso-login 2010-09-09 14:47:44 +0000
88@@ -3,8 +3,9 @@
89 # ubuntu-sso-login - Client side log-in utility for Ubuntu One
90 #
91 # Author: Rodney Dawes <rodney.dawes@canonical.com>
92+# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
93 #
94-# Copyright 2009 Canonical Ltd.
95+# Copyright 2009-2010 Canonical Ltd.
96 #
97 # This program is free software: you can redistribute it and/or modify it
98 # under the terms of the GNU General Public License version 3, as published
99@@ -18,196 +19,41 @@
100 # You should have received a copy of the GNU General Public License along
101 # with this program. If not, see <http://www.gnu.org/licenses/>.
102
103+"""Run the dbus service for UserManagement and ApplicationCredentials."""
104+
105 import gtk
106-import pango
107+import signal
108 import sys
109
110 import dbus.service
111
112 from dbus.mainloop.glib import DBusGMainLoop
113
114-from ubuntu_sso import DBUS_IFACE_AUTH_NAME, DBUS_BUS_NAME, DBUS_CRED_PATH
115-from ubuntu_sso.main import Login, SSOLogin, SSOCredentials
116-
117-from ubuntu_sso.logger import setupLogging
118-logger = setupLogging("ubuntu-sso-login")
119-
120+from ubuntu_sso import DBUS_BUS_NAME, DBUS_CRED_PATH
121+from ubuntu_sso.main import SSOLogin, SSOCredentials
122+
123+from ubuntu_sso.logger import setup_logging
124+
125+# Invalid name "ubuntu-sso-login"
126+# pylint: disable=C0103
127+
128+
129+logger = setup_logging("ubuntu-sso-login")
130 gtk.gdk.threads_init()
131 DBusGMainLoop(set_as_default=True)
132
133-OAUTH_REALM = "https://ubuntuone.com"
134-OAUTH_CONSUMER = "ubuntuone"
135-
136-NOTIFY_ICON_SIZE = 48
137-
138-# Why thank you GTK+ for enforcing style-set and breaking API
139-RCSTYLE = """
140-style 'dialogs' {
141- GtkDialog::action-area-border = 12
142- GtkDialog::button-spacing = 6
143- GtkDialog::content-area-border = 0
144-}
145-widget_class '*Dialog*' style 'dialogs'
146-"""
147-U1_PAIR_RECORD = "ubuntu_one_pair_record"
148-MAP_JS = """function(doc) {
149- if (doc.service_name == "ubuntuone") {
150- if (doc.application_annotations &&
151- doc.application_annotations["Ubuntu One"] &&
152- doc.application_annotations["Ubuntu One"]["%s"] &&
153- doc.application_annotations["Ubuntu One"]["%s"]["deleted"]) {
154- emit(doc._id, 1);
155- } else {
156- emit(doc._id, 0)
157- }
158- }
159-}""" % ("private_application_annotations", "private_application_annotations")
160-
161-
162-class LoginMain(object):
163- """Main login manager process class."""
164-
165- def __init__(self, *args, **kw):
166- """Initializes the child threads and dbus monitor."""
167- gtk.rc_parse_string(RCSTYLE)
168-
169- logger.info("Starting Ubuntu SSO login manager")
170-
171- self.__bus = dbus.SessionBus()
172-
173- def _connect_dbus_signals(self):
174- """Set up some signal handlers for DBus signals."""
175- self.__bus.add_signal_receiver(
176- handler_function=self.new_credentials,
177- signal_name="NewCredentials",
178- dbus_interface=DBUS_IFACE_AUTH_NAME)
179- self.__bus.add_signal_receiver(
180- handler_function=self.auth_denied,
181- signal_name="AuthorizationDenied",
182- dbus_interface=DBUS_IFACE_AUTH_NAME)
183- self.__bus.add_signal_receiver(
184- handler_function=self.got_oauth_error,
185- signal_name="OAuthError",
186- dbus_interface=DBUS_IFACE_AUTH_NAME)
187-
188- def new_credentials(self, realm=None, consumer_key=None, sender=None):
189- """Signal callback for when we get new credentials."""
190- self.set_up_desktopcouch_pairing(consumer_key)
191-
192- def got_port(*args, **kwargs):
193- # Discard the value. We don't really care about the port, here.
194- pass
195-
196- def got_error(*args, **kwargs):
197- logger.warn("On trying to start desktopcouch-service via DBus, "
198- "we got an error. %s %s" % (args, kwargs,))
199-
200- # We have auth, so start desktopcouch service by asking for its port.
201- dc_serv_proxy = self.__bus.get_object("org.desktopcouch.CouchDB", "/")
202- dc_serv_proxy.getPort(reply_handler=got_port, error_handler=got_error)
203-
204- def auth_denied(self):
205- """Signal callback for when auth was denied by user."""
206- logger.info("Access to Ubuntu One was denied.")
207-
208- from twisted.internet import reactor
209- reactor.stop()
210-
211- def got_oauth_error(self, message=None):
212- """Signal callback for when an OAuth error occured."""
213- def dialog_response(dialog, response):
214- """Handle the dialog closing."""
215- dialog.destroy()
216-
217- if message:
218- logger.error(message)
219- dialog = gtk.Dialog(title="Ubuntu One: Error",
220- flags=gtk.DIALOG_NO_SEPARATOR,
221- buttons=(gtk.STOCK_CLOSE,
222- gtk.RESPONSE_CLOSE))
223- dialog.set_default_response(gtk.RESPONSE_CLOSE)
224- dialog.set_icon_name("ubuntu-sso-client")
225-
226- area = dialog.get_content_area()
227-
228- hbox = gtk.HBox(spacing=12)
229- hbox.set_border_width(12)
230- area.pack_start(hbox)
231- hbox.show()
232-
233- image = gtk.Image()
234- image.set_from_icon_name("dialog-error", gtk.ICON_SIZE_DIALOG)
235- image.set_alignment(0.5, 0.0)
236- image.show()
237- hbox.pack_start(image, False, False)
238-
239- vbox = gtk.VBox(spacing=12)
240- vbox.show()
241- hbox.pack_start(vbox)
242-
243- label = gtk.Label("<b>Authorization Error</b>")
244- label.set_use_markup(True)
245- label.set_alignment(0.0, 0.5)
246- label.show()
247- vbox.pack_start(label, False, False)
248-
249- label = gtk.Label(message)
250- label.set_line_wrap(True)
251- label.set_max_width_chars(64)
252- label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
253- label.set_alignment(0.0, 0.0)
254- label.show()
255- vbox.pack_start(label, True, True)
256-
257- dialog.connect('close', dialog_response, gtk.RESPONSE_CLOSE)
258- dialog.connect('response', dialog_response)
259-
260- dialog.show()
261- else:
262- logger.error("Got an OAuth error with no message.")
263-
264- def set_up_desktopcouch_pairing(self, consumer_key):
265- """Add a pairing record between desktopcouch and Ubuntu One"""
266- try:
267- from desktopcouch.pair.couchdb_pairing.couchdb_io import \
268- put_static_paired_service
269- from desktopcouch.records.server import CouchDatabase
270- except ImportError:
271- # desktopcouch is not installed
272- logger.debug("Not adding desktopcouch pairing since desktopcouch "
273- "is not installed")
274- return
275- # Check whether there is already a record of the Ubuntu One service
276- db = CouchDatabase("management", create=True)
277- if not db.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD):
278- db.add_view(U1_PAIR_RECORD, MAP_JS, None, U1_PAIR_RECORD)
279- results = db.execute_view(U1_PAIR_RECORD, U1_PAIR_RECORD)
280- found = False
281- # Results should contain either one row or no rows
282- # If there is one row, its value will be 0, meaning that there is
283- # already an Ubuntu One pairing record, or 1, meaning that there
284- # was an Ubuntu One pairing record but it has since been unpaired
285- # Only create a new record if there is not one already. Specifically,
286- # do not add the record if there is a deleted one, as this means
287- # that the user explicitly unpaired it!
288- for row in results:
289- found = True
290- if row.value == 1:
291- logger.debug("Not adding desktopcouch pairing since the user "
292- "has explicitly unpaired with Ubuntu One")
293- else:
294- logger.debug("Not adding desktopcouch pairing since we are "
295- "already paired")
296- if not found:
297- put_static_paired_service(None, "ubuntuone")
298- logger.debug("Pairing desktopcouch with Ubuntu One")
299-
300- def main(self):
301- """Starts the gtk main loop."""
302- self._connect_dbus_signals()
303-
304- from twisted.internet import reactor
305- reactor.run()
306+
307+def sighup_handler(*a, **kw):
308+ """Stop the service.
309+
310+ This is not thread safe, see the link below for info:
311+ www.listware.net/201004/gtk-devel-list/115067-unix-signals-in-glib.html
312+ """
313+ from twisted.internet import reactor
314+ # Module 'twisted.internet.reactor' has no 'stop' member
315+ # pylint: disable=E1101
316+ logger.info("Stoping Ubuntu SSO login manager since SIGHUP was received.")
317+ reactor.stop()
318
319
320 if __name__ == "__main__":
321@@ -216,19 +62,24 @@
322 name = bus.request_name(DBUS_BUS_NAME,
323 dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
324 if name == dbus.bus.REQUEST_NAME_REPLY_EXISTS:
325- print "Ubuntu One login manager already running, quitting"
326+ logger.error("Ubuntu SSO login manager already running, quitting.")
327 sys.exit(0)
328
329+ logger.debug("Installing the Twisted gtk2reactor.")
330 from twisted.internet import gtk2reactor
331 gtk2reactor.install()
332
333- login = Login(dbus.service.BusName(DBUS_BUS_NAME,
334- bus=dbus.SessionBus()))
335- ssoLogin = SSOLogin(dbus.service.BusName(DBUS_BUS_NAME,
336- bus=dbus.SessionBus()))
337+ logger.debug("Hooking up SIGHUP with handler %r.", sighup_handler)
338+ signal.signal(signal.SIGHUP, sighup_handler)
339+
340+ logger.info("Starting Ubuntu SSO login manager for bus %r.", DBUS_BUS_NAME)
341+ SSOLogin(dbus.service.BusName(DBUS_BUS_NAME,
342+ bus=dbus.SessionBus()))
343 SSOCredentials(dbus.service.BusName(DBUS_BUS_NAME,
344 bus=dbus.SessionBus()),
345 object_path=DBUS_CRED_PATH)
346
347- manager = LoginMain()
348- manager.main()
349+ from twisted.internet import reactor
350+ # Module 'twisted.internet.reactor' has no 'run' member
351+ # pylint: disable=E1101
352+ reactor.run()
353
354=== removed file 'bin/ubuntu-sso-login-gui'
355--- bin/ubuntu-sso-login-gui 2010-08-24 15:47:58 +0000
356+++ bin/ubuntu-sso-login-gui 1970-01-01 00:00:00 +0000
357@@ -1,53 +0,0 @@
358-#!/usr/bin/python
359-
360-# ubuntu-sso-login-gui - GUI for registration and login for Ubuntu SSO
361-#
362-# Author: Natalia Bidart <natalia.bidart@canonical.com>
363-#
364-# Copyright 2010 Canonical Ltd.
365-#
366-# This program is free software: you can redistribute it and/or modify it
367-# under the terms of the GNU General Public License version 3, as published
368-# by the Free Software Foundation.
369-#
370-# This program is distributed in the hope that it will be useful, but
371-# WITHOUT ANY WARRANTY; without even the implied warranties of
372-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
373-# PURPOSE. See the GNU General Public License for more details.
374-#
375-# You should have received a copy of the GNU General Public License along
376-# with this program. If not, see <http://www.gnu.org/licenses/>.
377-"""Script to run the Ubuntu SSO client GUI."""
378-
379-import gtk
380-import sys
381-
382-from ubuntu_sso.gui import UbuntuSSOClientGUI
383-
384-
385-APP_NAME = 'Ubuntu'
386-TC_URI = 'http://one.ubuntu.com/terms'
387-HELP_TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' \
388- 'Nam sed lorem nibh. Suspendisse gravida nulla non nunc suscipit' \
389- ' pulvinar tempus ut augue.'
390-
391-main_quit = lambda *args, **kwargs: gtk.main_quit()
392-
393-
394-if __name__ == '__main__':
395- win = gtk.Window()
396- win.set_title(APP_NAME)
397- win.set_position(gtk.WIN_POS_CENTER)
398- win.set_size_request(800, 600)
399- win.show()
400- xid = win.window.xid
401-
402- if len(sys.argv) > 1 and sys.argv[1] == '--login':
403- app = UbuntuSSOClientGUI(close_callback=main_quit, app_name=APP_NAME,
404- tc_uri=None, help_text=HELP_TEXT,
405- window_id=xid, login_only=True)
406- else:
407- app = UbuntuSSOClientGUI(close_callback=main_quit, app_name=APP_NAME,
408- tc_uri=TC_URI, help_text=HELP_TEXT,
409- window_id=xid)
410- gtk.main()
411
412=== modified file 'contrib/dbus_util.py'
413--- contrib/dbus_util.py 2010-08-24 15:47:58 +0000
414+++ contrib/dbus_util.py 2010-09-09 14:47:44 +0000
415@@ -1,7 +1,6 @@
416 #
417 # Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
418 #
419-# Copyright 2009-2010 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@@ -15,6 +14,8 @@
424 # You should have received a copy of the GNU General Public License along
425 # with this program. If not, see <http://www.gnu.org/licenses/>.
426
427+"""Utilies to run a separated DBus session."""
428+
429 import os
430 import signal
431 import subprocess
432@@ -33,6 +34,9 @@
433
434
435 class DBusRunner(object):
436+ """A DBus runner."""
437+
438+ # pylint: disable=C0103
439
440 def __init__(self):
441 self.dbus_address = None
442
443=== modified file 'contrib/test'
444--- contrib/test 2010-06-22 14:18:04 +0000
445+++ contrib/test 2010-09-09 14:47:44 +0000
446@@ -32,7 +32,7 @@
447 class TestRunner(object):
448
449 def _load_unittest(self, relpath):
450- """Load unit tests from a Python module with the given relative path."""
451+ """Load unittests from a Python module with the given relative path."""
452 assert relpath.endswith(".py"), (
453 "%s does not appear to be a Python module" % relpath)
454 modpath = relpath.replace(os.path.sep, ".")[:-3]
455@@ -53,7 +53,7 @@
456 if test_pattern:
457 pattern = re.compile('.*%s.*' % test_pattern)
458 else:
459- pattern = None
460+ pattern = None
461
462 if testpath:
463 module_suite = self._load_unittest(testpath)
464@@ -67,7 +67,7 @@
465 return suite
466
467 # We don't use the dirs variable, so ignore the warning
468- # pylint: disable-msg=W0612
469+ # pylint: disable=W0612
470 for root, dirs, files in os.walk("ubuntu_sso"):
471 for file in files:
472 path = os.path.join(root, file)
473@@ -130,9 +130,9 @@
474 usage = '%prog [options] path'
475 parser = OptionParser(usage=usage)
476 parser.add_option("-t", "--test", dest="test",
477- help = "run specific tests, e.g: className.methodName")
478+ help="run specific tests, e.g: className.methodName")
479 parser.add_option("-l", "--loop", dest="loops", type="int", default=1,
480- help = "loop selected tests LOOPS number of times",
481+ help="loop selected tests LOOPS number of times",
482 metavar="LOOPS")
483
484 (options, args) = parser.parse_args()
485@@ -143,4 +143,4 @@
486 sys.exit()
487 else:
488 testpath = None
489- TestRunner().run(testpath, options.test, options.loops)
490+ TestRunner().run(testpath, options.test, options.loops)
491
492=== modified file 'contrib/testing/__init__.py'
493--- contrib/testing/__init__.py 2010-08-06 22:01:01 +0000
494+++ contrib/testing/__init__.py 2010-09-09 14:47:44 +0000
495@@ -1,1 +1,1 @@
496-"""Testing utilities for Ubuntu One client code."""
497+"""Testing utilities for Ubuntu SSO code."""
498
499=== modified file 'contrib/testing/testcase.py'
500--- contrib/testing/testcase.py 2010-08-11 14:11:57 +0000
501+++ contrib/testing/testcase.py 2010-09-09 14:47:44 +0000
502@@ -1,5 +1,6 @@
503 #
504 # Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
505+# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
506 #
507 # Copyright 2009-2010 Canonical Ltd.
508 #
509@@ -14,102 +15,23 @@
510 #
511 # You should have received a copy of the GNU General Public License along
512 # with this program. If not, see <http://www.gnu.org/licenses/>.
513-""" Base tests cases and test utilities """
514-from __future__ import with_statement
515+"""Base tests cases and test utilities."""
516+
517+import logging
518
519 import dbus
520+
521 from dbus.mainloop.glib import DBusGMainLoop
522-import contextlib
523-import logging
524-import os
525-import shutil
526-import itertools
527-
528-from ubuntu_sso import DBUS_IFACE_AUTH_NAME
529-from ubuntu_sso.main import Login, LoginProcessor
530-
531 from twisted.internet import defer
532 from twisted.python import failure
533-from unittest import TestCase
534-from zope.interface import implements
535-from oauth import oauth
536-
537-
538-@contextlib.contextmanager
539-def environ(env_var, new_value):
540- """context manager to replace/add an environ value"""
541- old_value = os.environ.get(env_var, None)
542- os.environ[env_var] = new_value
543- yield
544- if old_value is None:
545- os.environ.pop(env_var)
546- else:
547- os.environ[env_var] = old_value
548-
549-
550-class BaseTestCase(TestCase):
551- """Base TestCase with helper methods to handle temp dir.
552-
553- This class provides:
554- mktemp(name): helper to create temporary dirs
555- rmtree(path): support read-only shares
556- makedirs(path): support read-only shares
557- """
558-
559- def mktemp(self, name='temp'):
560- """Customized mktemp that accepts an optional name argument. """
561- tempdir = os.path.join(self.tmpdir, name)
562- if os.path.exists(tempdir):
563- self.rmtree(tempdir)
564- self.makedirs(tempdir)
565- return tempdir
566-
567- @property
568- def tmpdir(self):
569- """Default tmpdir: module/class/test_method."""
570- # check if we already generated the root path
571- root_dir = getattr(self, '__root', None)
572- if root_dir:
573- return root_dir
574- MAX_FILENAME = 32 # some platforms limit lengths of filenames
575- base = os.path.join(self.__class__.__module__[:MAX_FILENAME],
576- self.__class__.__name__[:MAX_FILENAME],
577- self._testMethodName[:MAX_FILENAME])
578- # use _trial_temp dir, it should be os.gwtcwd()
579- # define the root temp dir of the testcase, pylint: disable-msg=W0201
580- self.__root = os.path.join(os.getcwd(), base)
581- return self.__root
582-
583- def rmtree(self, path):
584- """Custom rmtree that handle ro parent(s) and childs."""
585- if not os.path.exists(path):
586- return
587- # change perms to rw, so we can delete the temp dir
588- if path != getattr(self, '__root', None):
589- os.chmod(os.path.dirname(path), 0755)
590- if not os.access(path, os.W_OK):
591- os.chmod(path, 0755)
592- # pylint: disable-msg=W0612
593- for dirpath, dirs, files in os.walk(path):
594- for dir in dirs:
595- if not os.access(os.path.join(dirpath, dir), os.W_OK):
596- os.chmod(os.path.join(dirpath, dir), 0777)
597- shutil.rmtree(path)
598-
599- def makedirs(self, path):
600- """Custom makedirs that handle ro parent."""
601- parent = os.path.dirname(path)
602- if os.path.exists(parent):
603- os.chmod(parent, 0755)
604- os.makedirs(path)
605-
606-
607-class DBusTestCase(BaseTestCase):
608- """ Test the DBus event handling """
609+from twisted.trial.unittest import TestCase
610+
611+
612+class DBusTestCase(TestCase):
613+ """Test the DBus event handling."""
614
615 def setUp(self):
616- """ Setup the infrastructure fo the test (dbus service). """
617- BaseTestCase.setUp(self)
618+ """Setup the infrastructure fo the test (dbus service)."""
619 self.loop = DBusGMainLoop(set_as_default=True)
620 self.bus = dbus.bus.BusConnection(mainloop=self.loop)
621 # monkeypatch busName.__del__ to avoid errors on gc
622@@ -119,31 +41,29 @@
623 self.signal_receivers = set()
624
625 def tearDown(self):
626- """ Cleanup the test. """
627+ """Cleanup the test."""
628 d = self.cleanup_signal_receivers(self.signal_receivers)
629- d.addBoth(self._tearDown)
630- d.addBoth(lambda _: BaseTestCase.tearDown(self))
631+ d.addBoth(self._tear_down)
632 return d
633
634- def _tearDown(self, *args):
635- """ shutdown """
636- self.dbus_iface.bus.flush()
637+ def _tear_down(self, _):
638+ """Shutdown."""
639 self.bus.flush()
640 self.bus.close()
641
642 def error_handler(self, error):
643- """ default error handler for DBus calls. """
644+ """Default error handler for DBus calls."""
645 if isinstance(error, failure.Failure):
646 self.fail(error.getErrorMessage())
647
648 def cleanup_signal_receivers(self, signal_receivers):
649- """ cleanup self.signal_receivers and returns a deferred """
650+ """Cleanup self.signal_receivers and returns a deferred."""
651 deferreds = []
652 for match in signal_receivers:
653 d = defer.Deferred()
654
655 def callback(*args):
656- """ callback that accepts *args. """
657+ """Callback that accepts *args."""
658 if not d.called:
659 d.callback(args)
660
661@@ -158,62 +78,6 @@
662 return defer.succeed(True)
663
664
665-class FakeLoginProcessor(LoginProcessor):
666- """Stub login processor."""
667-
668- def __init__(self, dbus_object):
669- """Initialize the login processor."""
670- LoginProcessor.__init__(self, dbus_object)
671- self.next_login_cb = None
672-
673- def login(self, realm, consumer_key, error_handler=None,
674- reply_handler=None, do_login=True):
675- """Stub, call self.next_login_cb or send NewCredentials if
676- self.next_login_cb isn't defined.
677- """
678- self.realm = str(realm)
679- self.consumer_key = str(consumer_key)
680- if self.next_login_cb:
681- cb = self.next_login_cb[0]
682- args = self.next_login_cb[1]
683- self.next_login_cb = None
684- return cb(*args)
685- else:
686- self.dbus_object.NewCredentials(realm, consumer_key)
687-
688- def clear_token(self, realm, consumer_key):
689- """Stub, do nothing"""
690- pass
691-
692- def next_login_with(self, callback, args=tuple()):
693- """shortcircuit the next call to login and call the specified callback.
694- callback is usually one of: self.got_token, self.got_no_token,
695- self.got_denial or self.got_error.
696- """
697- self.next_login_cb = (callback, args)
698-
699-
700-class FakeLogin(Login):
701- """Stub Object which listens for D-Bus OAuth requests"""
702-
703- def __init__(self, bus):
704- """Initiate the object."""
705- self.bus = bus
706- self.busName = dbus.service.BusName(DBUS_IFACE_AUTH_NAME, bus=self.bus)
707- # bypass the parent class __init__ as it has the path hardcoded
708- # and we can't use '/' as the path, as we are already using it
709- # for syncdaemon. pylint: disable-msg=W0233,W0231
710- dbus.service.Object.__init__(self, object_path="/oauthdesktop",
711- bus_name=self.busName)
712- self.processor = FakeLoginProcessor(self)
713- self.currently_authing = False
714-
715- def shutdown(self):
716- """Shutdown and remove any trace from the bus"""
717- self.busName.get_bus().release_name(self.busName.get_name())
718- self.remove_from_connection()
719-
720-
721 class MementoHandler(logging.Handler):
722 """A handler class which store logging records in a list."""
723
724@@ -226,9 +90,9 @@
725 """Just add the record to self.records."""
726 self.records.append(record)
727
728- def check(self, level, msg):
729- """Check that something is logged."""
730+ def check(self, level, *msgs):
731+ """Verifies that the msgs are logged in the specified level."""
732 for rec in self.records:
733- if rec.levelno == level and str(msg) in rec.message:
734+ if rec.levelno == level and all(m in rec.message for m in msgs):
735 return True
736 return False
737
738=== removed directory 'data/oauth_registration.d'
739=== removed file 'data/oauth_registration.d/ubuntuone'
740--- data/oauth_registration.d/ubuntuone 2010-06-16 15:11:04 +0000
741+++ data/oauth_registration.d/ubuntuone 1970-01-01 00:00:00 +0000
742@@ -1,23 +0,0 @@
743-[storagefs-live]
744-realm = https://ubuntuone.com
745-consumer_key = ubuntuone
746-exe_path = /usr/bin/python
747-application_name = ubuntuone-syncdaemon
748-
749-[storagefs-local]
750-realm = http://localhost
751-consumer_key = ubuntuone
752-exe_path = /usr/bin/python
753-application_name = ubuntuone-syncdaemon
754-
755-[control-panel]
756-realm = https://ubuntuone.com
757-consumer_key = ubuntuone
758-exe_path = /usr/bin/python
759-application_name = ubuntuone-preferences
760-
761-[launcher]
762-realm = https://ubuntuone.com
763-consumer_key = ubuntuone
764-exe_path = /usr/bin/python
765-application_name = ubuntuone-launch
766
767=== removed file 'data/oauth_urls'
768--- data/oauth_urls 2010-06-16 15:11:04 +0000
769+++ data/oauth_urls 1970-01-01 00:00:00 +0000
770@@ -1,5 +0,0 @@
771-[default]
772-request_token_url = /oauth/request/
773-user_authorisation_url = /oauth/authorize/
774-access_token_url = /oauth/access/
775-consumer_secret=hammertime
776
777=== modified file 'debian/changelog'
778--- debian/changelog 2010-08-30 17:10:34 +0000
779+++ debian/changelog 2010-09-09 14:47:44 +0000
780@@ -1,3 +1,58 @@
781+ubuntu-sso-client (0.99.6-0ubuntu1) UNRELEASED; urgency=low
782+
783+ * New upstream release (0.99.6):
784+
785+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
786+ * Restoring NoAccessToken, DBUS_IFACE_AUTH_NAME, and DBUS_PATH_AUTH to avoid
787+ API breakage on old versions of ubuntuone-client (LP: #633220).
788+
789+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
790+ * Added handler for SIGHUP to stop the service (LP: #633300).
791+
792+ * New upstream release (0.99.5):
793+
794+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
795+ * Signals from the DBus "UserManagement" service are now filtered by app
796+ name (LP: #629025).
797+
798+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
799+ * Fixed indentantion block (LP: #632043).
800+
801+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
802+ * Removed all the old auth mechanism (LP: #616121).
803+ * Added pylint check, fixed all pylint errors.
804+
805+ [John R. Lenton <john.lenton@canonical.com>]
806+ * Enable setting USSOC_PING_URL and USSOC_SERVICE_URL to override the
807+ (otherwise hardcoded) ping url and service url in ussoc (LP: #616110).
808+
809+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
810+ * Hiding unused fields to make the registration UI a bit shorter (LP:
811+ #627440).
812+
813+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
814+ * U1 url to ping server with tokens needs to be https (LP: #627700).
815+
816+ [Alejandro J. Cura <alecu@canonical.com>]
817+ * Disabled support for plugins for the embedded webkit (LP: #617041)
818+
819+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
820+ * Token name is now built using '@'. Old quoted tokens are migrated to
821+ the new names (LP: #628158).
822+
823+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
824+ * Pages have now a default_widget attribute that is the default widget
825+ when the page is displayed to the user (LP: #62552).
826+
827+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
828+ * User-cancelation signal is sent when the window is close and no other
829+ event was generated (LP: #627419).
830+
831+ [Natalia B. Bidart <natalia.bidart@canonical.com>]
832+ * Made DBus signal connection more robust (LP: #624127).
833+
834+ -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Wed, 08 Sep 2010 16:25:02 -0300
835+
836 ubuntu-sso-client (0.99.4-0ubuntu1) maverick; urgency=low
837
838 * New upstream release:
839
840=== added file 'debian/postinst'
841--- debian/postinst 1970-01-01 00:00:00 +0000
842+++ debian/postinst 2010-09-09 14:47:44 +0000
843@@ -0,0 +1,25 @@
844+#!/bin/sh
845+set -e
846+
847+LASTVERSION="0.99.4-0ubuntu1"
848+
849+dpkg-maintscript-helper rm_conffile \
850+/etc/xdg/ubuntu-sso/oauth_registration.d/ubuntuone "$LASTVERSION" -- "$@"
851+
852+dpkg-maintscript-helper rm_conffile \
853+/etc/xdg/ubuntu-sso/oauth_urls "$LASTVERSION" -- "$@"
854+
855+rm_confdir() {
856+ local DIR="$1"
857+ if [ -d $DIR ]; then rmdir $DIR; fi || true
858+}
859+
860+case "$1" in
861+configure)
862+ if dpkg --compare-versions "$2" le "$LASTVERSION"; then
863+ rm_confdir "/etc/xdg/ubuntu-sso/oauth_registration.d"
864+ rm_confdir "/etc/xdg/ubuntu-sso/"
865+ fi
866+esac
867+
868+#DEBHELPER#
869
870=== added file 'debian/postrm'
871--- debian/postrm 1970-01-01 00:00:00 +0000
872+++ debian/postrm 2010-09-09 14:47:44 +0000
873@@ -0,0 +1,15 @@
874+#!/bin/sh
875+set -e
876+
877+LASTVERSION="0.99.4-0ubuntu1"
878+
879+dpkg-maintscript-helper rm_conffile \
880+/etc/xdg/ubuntu-sso/oauth_registration.d/ubuntuone "$LASTVERSION" -- "$@"
881+
882+dpkg-maintscript-helper rm_conffile \
883+/etc/xdg/ubuntu-sso/oauth_urls "$LASTVERSION" -- "$@"
884+
885+rmdir "/etc/xdg/ubuntu-sso/oauth_registration.d/" 2>/dev/null || true
886+rmdir "/etc/xdg/ubuntu-sso/" 2>/dev/null || true
887+
888+#DEBHELPER#
889
890=== added file 'debian/preinst'
891--- debian/preinst 1970-01-01 00:00:00 +0000
892+++ debian/preinst 2010-09-09 14:47:44 +0000
893@@ -0,0 +1,14 @@
894+#!/bin/sh
895+set -e
896+
897+LASTVERSION="0.99.4-0ubuntu1"
898+
899+pkill -HUP ubuntu-sso-login || true
900+
901+dpkg-maintscript-helper rm_conffile \
902+/etc/xdg/ubuntu-sso/oauth_registration.d/ubuntuone "$LASTVERSION" -- "$@"
903+
904+dpkg-maintscript-helper rm_conffile \
905+/etc/xdg/ubuntu-sso/oauth_urls "$LASTVERSION" -- "$@"
906+
907+#DEBHELPER#
908
909=== added file 'pylintrc'
910--- pylintrc 1970-01-01 00:00:00 +0000
911+++ pylintrc 2010-09-09 14:47:44 +0000
912@@ -0,0 +1,309 @@
913+# lint Python modules using external checkers.
914+#
915+# This is the main checker controlling the other ones and the reports
916+# generation. It is itself both a raw checker and an astng checker in order
917+# to:
918+# * handle message activation / deactivation at the module level
919+# * handle some basic but necessary stats'data (number of classes, methods...)
920+#
921+[MASTER]
922+
923+# Specify a configuration file.
924+#rcfile=
925+
926+# Python code to execute, usually for sys.path manipulation such as
927+# pygtk.require().
928+#init-hook=
929+
930+# Profiled execution.
931+profile=no
932+
933+# Add <file or directory> to the black list. It should be a base name, not a
934+# path. You may set this option multiple times.
935+#ignore=<somedir>
936+
937+# Pickle collected data for later comparisons.
938+persistent=no
939+
940+# Set the cache size for astng objects.
941+cache-size=500
942+
943+# List of plugins (as comma separated values of python modules names) to load,
944+# usually to register additional checkers.
945+load-plugins=
946+
947+
948+[MESSAGES CONTROL]
949+
950+# Enable only checker(s) with the given id(s). This option conflicts with the
951+# disable-checker option
952+#enable-checker=
953+
954+# Enable all checker(s) except those with the given id(s). This option
955+# conflicts with the enable-checker option
956+#disable-checker=
957+
958+# Enable all messages in the listed categories.
959+#enable-cat=
960+
961+# Disable all messages in the listed categories.
962+#disable-cat=
963+
964+# Disable the message(s) with the given id(s) or categories
965+# W0142: *Used * or ** magic*
966+# W0613: Unused argument 'yyy'
967+# C0302: Too many lines in module
968+disable=R,I,W0142,W0613,C0302
969+
970+
971+[REPORTS]
972+
973+# Set the output format. Available formats are text, parseable, colorized, msvs
974+# (visual studio) and html
975+output-format=colorized
976+
977+# Include message's id in output
978+include-ids=yes
979+
980+# Put messages in a separate file for each module / package specified on the
981+# command line instead of printing them on stdout. Reports (if any) will be
982+# written in a file name "pylint_global.[txt|html]".
983+files-output=no
984+
985+# Tells whether to display a full report or only the messages
986+reports=no
987+
988+# Python expression which should return a note less than 10 (10 is the highest
989+# note). You have access to the variables errors warning, statement which
990+# respectively contain the number of errors / warnings messages and the total
991+# number of statements analyzed. This is used by the global evaluation report
992+# (R0004).
993+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
994+
995+# Add a comment according to your evaluation note. This is used by the global
996+# evaluation report (R0004).
997+comment=no
998+
999+# Enable the report(s) with the given id(s).
1000+#enable-report=
1001+
1002+# Disable the report(s) with the given id(s).
1003+#disable-report=
1004+
1005+
1006+# try to find bugs in the code using type inference
1007+#
1008+[TYPECHECK]
1009+
1010+# Tells whether missing members accessed in mixin class should be ignored. A
1011+# mixin class is detected if its name ends with "mixin" (case insensitive).
1012+ignore-mixin-members=yes
1013+
1014+# List of classes names for which member attributes should not be checked
1015+# (useful for classes with attributes dynamically set).
1016+ignored-classes=
1017+
1018+# When zope mode is activated, add a predefined set of Zope acquired attributes
1019+# to generated-members.
1020+zope=no
1021+
1022+# List of members which are set dynamically and missed by pylint inference
1023+# system, and so shouldn't trigger E0201 when accessed.
1024+generated-members=REQUEST,acl_users,aq_parent
1025+
1026+
1027+# checks for
1028+# * unused variables / imports
1029+# * undefined variables
1030+# * redefinition of variable from builtins or from an outer scope
1031+# * use of variable before assignment
1032+#
1033+[VARIABLES]
1034+
1035+# Tells whether we should check for unused import in __init__ files.
1036+init-import=yes
1037+
1038+# A regular expression matching names used for dummy variables (i.e. not used).
1039+dummy-variables-rgx=_|dummy
1040+
1041+# List of additional names supposed to be defined in builtins. Remember that
1042+# you should avoid to define new builtins when possible.
1043+additional-builtins=
1044+
1045+
1046+# checks for :
1047+# * doc strings
1048+# * modules / classes / functions / methods / arguments / variables name
1049+# * number of arguments, local variables, branches, returns and statements in
1050+# functions, methods
1051+# * required module attributes
1052+# * dangerous default values as arguments
1053+# * redefinition of function / method / class
1054+# * uses of the global statement
1055+#
1056+[BASIC]
1057+
1058+# Required attributes for module, separated by a comma
1059+required-attributes=
1060+
1061+# Regular expression which should only match functions or classes name which do
1062+# not require a docstring
1063+no-docstring-rgx=(__.*__|setUp|tearDown)
1064+
1065+# Regular expression which should only match correct module names
1066+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
1067+
1068+# Regular expression which should only match correct module level names
1069+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
1070+
1071+# Regular expression which should only match correct class names
1072+class-rgx=[A-Z_][a-zA-Z0-9]+$
1073+
1074+# Regular expression which should only match correct function names
1075+function-rgx=[a-z_][a-z0-9_]{2,79}$
1076+
1077+# Regular expression which should only match correct method names
1078+method-rgx=([a-z_][a-z0-9_]{2,79}$|setUp|tearDown)
1079+
1080+# Regular expression which should only match correct instance attribute names
1081+attr-rgx=[a-z_][a-z0-9_]{1,30}$
1082+
1083+# Regular expression which should only match correct argument names
1084+argument-rgx=[a-z_][a-z0-9_]{1,30}$
1085+
1086+# Regular expression which should only match correct variable names
1087+variable-rgx=[a-z_][a-z0-9_]{1,30}$
1088+
1089+# Regular expression which should only match correct list comprehension /
1090+# generator expression variable names
1091+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
1092+
1093+# Good variable names which should always be accepted, separated by a comma
1094+good-names=d,e,f,g,i,j,k,ex,logger,Run,_
1095+
1096+# Bad variable names which should always be refused, separated by a comma
1097+bad-names=foo,bar,baz,toto,tutu,tata
1098+
1099+# List of builtins function names that should not be used, separated by a comma
1100+bad-functions=apply,input
1101+
1102+
1103+# checks for sign of poor/misdesign:
1104+# * number of methods, attributes, local variables...
1105+# * size, complexity of functions, methods
1106+#
1107+[DESIGN]
1108+
1109+# Maximum number of arguments for function / method
1110+max-args=5
1111+
1112+# Maximum number of locals for function / method body
1113+max-locals=15
1114+
1115+# Maximum number of return / yield for function / method body
1116+max-returns=6
1117+
1118+# Maximum number of branch for function / method body
1119+max-branchs=12
1120+
1121+# Maximum number of statements in function / method body
1122+max-statements=50
1123+
1124+# Maximum number of parents for a class (see R0901).
1125+max-parents=7
1126+
1127+# Maximum number of attributes for a class (see R0902).
1128+max-attributes=7
1129+
1130+# Minimum number of public methods for a class (see R0903).
1131+min-public-methods=2
1132+
1133+# Maximum number of public methods for a class (see R0904).
1134+max-public-methods=20
1135+
1136+
1137+# checks for :
1138+# * methods without self as first argument
1139+# * overridden methods signature
1140+# * access only to existent members via self
1141+# * attributes not defined in the __init__ method
1142+# * supported interfaces implementation
1143+# * unreachable code
1144+#
1145+[CLASSES]
1146+
1147+# List of interface methods to ignore, separated by a comma. This is used for
1148+# instance to not check methods defines in Zopes Interface base class.
1149+#ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by,providedBy
1150+
1151+# List of method names used to declare (i.e. assign) instance attributes.
1152+defining-attr-methods=__init__,__new__,setUp
1153+
1154+
1155+# checks for
1156+# * external modules dependencies
1157+# * relative / wildcard imports
1158+# * cyclic imports
1159+# * uses of deprecated modules
1160+#
1161+[IMPORTS]
1162+
1163+# Deprecated modules which should not be used, separated by a comma
1164+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
1165+
1166+# Create a graph of every (i.e. internal and external) dependencies in the
1167+# given file (report RP0402 must not be disabled)
1168+import-graph=
1169+
1170+# Create a graph of external dependencies in the given file (report RP0402 must
1171+# not be disabled)
1172+ext-import-graph=
1173+
1174+# Create a graph of internal dependencies in the given file (report RP0402 must
1175+# not be disabled)
1176+int-import-graph=
1177+
1178+
1179+# checks for :
1180+# * unauthorized constructions
1181+# * strict indentation
1182+# * line length
1183+# * use of <> instead of !=
1184+#
1185+[FORMAT]
1186+
1187+# Maximum number of characters on a single line.
1188+max-line-length=79
1189+
1190+# Maximum number of lines in a module
1191+max-module-lines=2000
1192+
1193+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
1194+# tab).
1195+indent-string=' '
1196+
1197+
1198+# checks for similarities and duplicated code. This computation may be
1199+# memory / CPU intensive, so you should disable it if you experiments some
1200+# problems.
1201+#
1202+[SIMILARITIES]
1203+
1204+# Minimum lines number of a similarity.
1205+min-similarity-lines=4
1206+
1207+# Ignore comments when computing similarities.
1208+ignore-comments=yes
1209+
1210+# Ignore docstrings when computing similarities.
1211+ignore-docstrings=yes
1212+
1213+
1214+# checks for:
1215+# * warning notes in the code like FIXME, XXX
1216+# * PEP 263: source code with non ascii character but no encoding declaration
1217+#
1218+[MISCELLANEOUS]
1219+
1220+# List of note tags to take in consideration, separated by a comma.
1221+notes=FIXME,XXX,TODO,fixme,xxx,todo
1222
1223=== modified file 'run-tests'
1224--- run-tests 2010-08-26 01:07:41 +0000
1225+++ run-tests 2010-09-09 14:47:44 +0000
1226@@ -16,12 +16,9 @@
1227 # with this program. If not, see <http://www.gnu.org/licenses/>.
1228
1229 `which xvfb-run` ./contrib/test "$@"
1230-pyflakes bin ubuntu_sso
1231+pylint contrib ubuntu_sso
1232 if [ -x `which pep8` ]; then
1233- pep8 --repeat bin/ contrib/ \
1234- ubuntu_sso/gui.py ubuntu_sso/main.py ubuntu_sso/keyring.py \
1235- ubuntu_sso/tests/test_gui.py ubuntu_sso/tests/test_main.py \
1236- ubuntu_sso/tests/test_keyring.py
1237+ pep8 --repeat bin/ contrib/ ubuntu_sso/
1238 else
1239 echo "Please install the 'pep8' package."
1240 fi
1241
1242=== modified file 'setup.py'
1243--- setup.py 2010-08-27 22:16:40 +0000
1244+++ setup.py 2010-09-09 14:47:44 +0000
1245@@ -1,6 +1,8 @@
1246 #!/usr/bin/env python
1247 # setup.py - Build system for Ubuntu SSO Client package
1248 #
1249+# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
1250+#
1251 # Copyright 2010 Canonical Ltd.
1252 #
1253 # This program is free software: you can redistribute it and/or modify it
1254@@ -36,6 +38,7 @@
1255 # Defining variables for various rules here, similar to a Makefile.am
1256 CLEANFILES = ['data/com.ubuntu.sso.service']
1257
1258+
1259 # XXX: This needs some serious cleanup
1260 class SSOBuild(build_extra.build_extra):
1261 """Class to build the extra files."""
1262@@ -53,7 +56,8 @@
1263 out_file = 'data/com.ubuntu.sso.service'
1264 replaced_path = '/usr/lib/ubuntu-sso-client'
1265
1266- os.system("%(cmd)s -e 's|\@libexecdir\@|%(rep)s|g' < %(in)s > %(out)s" %
1267+ cmd = "%(cmd)s -e 's|\@libexecdir\@|%(rep)s|g' < %(in)s > %(out)s"
1268+ os.system( cmd %
1269 {'cmd' : sed,
1270 'rep' : replaced_path,
1271 'in' : in_file,
1272@@ -82,7 +86,7 @@
1273
1274 DistUtilsExtra.auto.setup(
1275 name='ubuntu-sso-client',
1276- version='0.99.4',
1277+ version='0.99.6',
1278 license='GPL v3',
1279 author='Natalia Bidart',
1280 author_email='natalia.bidart@canonical.com',
1281@@ -92,16 +96,9 @@
1282 url='https://launchpad.net/ubuntu-sso-client',
1283 packages=['ubuntu_sso'],
1284 data_files=[
1285- ('share/dbus-1/services', ['data/com.ubuntu.sso.service',]),
1286- ('lib/ubuntu-sso-client', ['bin/ubuntu-sso-login',]),
1287- ('share/ubuntu-sso-client/data', ['data/ui.glade',]),
1288- ('/etc/xdg/ubuntu-sso/', ['data/oauth_urls',]),
1289- ('/etc/xdg/ubuntu-sso/oauth_registration.d',
1290- ['data/oauth_registration.d/ubuntuone',]),
1291- ],
1292-
1293- cmdclass = {
1294+ ('share/dbus-1/services', ['data/com.ubuntu.sso.service']),
1295+ ('lib/ubuntu-sso-client', ['bin/ubuntu-sso-login']),
1296+ ('share/ubuntu-sso-client/data', ['data/ui.glade'])],
1297+ cmdclass={
1298 'build' : SSOBuild,
1299- 'clean' : SSOClean,
1300- },
1301-)
1302+ 'clean' : SSOClean})
1303
1304=== modified file 'ubuntu_sso/__init__.py'
1305--- ubuntu_sso/__init__.py 2010-08-11 17:03:11 +0000
1306+++ ubuntu_sso/__init__.py 2010-09-09 14:47:44 +0000
1307@@ -1,6 +1,6 @@
1308-# ubuntu_sso - OAuth client support for desktop apps
1309+# ubuntu_sso - Ubuntu Single Sign On client support for desktop apps
1310 #
1311-# Copyright 2009 Canonical Ltd.
1312+# Copyright 2009-2010 Canonical Ltd.
1313 #
1314 # This program is free software: you can redistribute it and/or modify it
1315 # under the terms of the GNU General Public License version 3, as published
1316@@ -13,7 +13,7 @@
1317 #
1318 # You should have received a copy of the GNU General Public License along
1319 # with this program. If not, see <http://www.gnu.org/licenses/>.
1320-"""OAuth client authorisation code."""
1321+"""Ubuntu Single Sign On client code."""
1322
1323 # constants
1324 DBUS_PATH_AUTH = "/"
1325
1326=== modified file 'ubuntu_sso/auth.py'
1327--- ubuntu_sso/auth.py 2010-08-11 17:03:11 +0000
1328+++ ubuntu_sso/auth.py 2010-09-09 14:47:44 +0000
1329@@ -11,475 +11,15 @@
1330 # This program is distributed in the hope that it will be useful, but
1331 # WITHOUT ANY WARRANTY; without even the implied warranties of
1332 # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1333-# PURPOSE. See the GNU General Public License for more details.
1334+# PURPOSE. See the GNU General Public License for more details.
1335 #
1336 # You should have received a copy of the GNU General Public License along
1337-# with this program. If not, see <http://www.gnu.org/licenses/>.
1338-"""OAuth client authorisation code.
1339+# with this program. If not, see <http://www.gnu.org/licenses/>.
1340+"""This is an obsolete module.
1341
1342-This code handles acquisition of an OAuth access token for a service,
1343-managed through the GNOME keyring for future use, and asynchronously.
1344+Do not use it, use the DBus API from main instead (SSOCredentials).
1345 """
1346
1347-__metaclass__ = type
1348-
1349-import dbus
1350-import os
1351-import random
1352-import subprocess
1353-import socket, httplib, urllib
1354-import sys
1355-
1356-import gnomekeyring
1357-from oauth import oauth
1358-try:
1359- from ubuntuone.clientdefs import VERSION
1360- ubuntuone_client_version = VERSION
1361-except ImportError:
1362- ubuntuone_client_version = "Unknown"
1363-from ubuntu_sso.key_acls import set_all_key_acls
1364-
1365-from threading import Thread
1366-from twisted.internet import reactor
1367-from twisted.web import server, resource
1368-
1369-from ubuntu_sso.logger import setupLogging
1370-logger = setupLogging("ubuntu_sso.auth")
1371-
1372
1373 class NoAccessToken(Exception):
1374 """No access token available."""
1375-
1376-# NetworkManager State constants
1377-NM_STATE_UNKNOWN = 0
1378-NM_STATE_ASLEEP = 1
1379-NM_STATE_CONNECTING = 2
1380-NM_STATE_CONNECTED = 3
1381-NM_STATE_DISCONNECTED = 4
1382-
1383-# Monkeypatch httplib so that urllib will fail on invalid certificate
1384-# Only patch if we can import ssl to work around fail in 2.5
1385-try:
1386- import ssl
1387-except ImportError:
1388- pass
1389-else:
1390- def _connect_wrapper(self):
1391- """Override HTTPSConnection.connect to require certificate checks"""
1392- sock = socket.create_connection((self.host, self.port), self.timeout)
1393- try:
1394- if self._tunnel_host:
1395- self.sock = sock
1396- self._tunnel()
1397- except AttributeError:
1398- pass
1399- self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
1400- cert_reqs=ssl.CERT_REQUIRED,
1401- ca_certs="/etc/ssl/certs/ca-certificates.crt")
1402- httplib.HTTPSConnection.connect = _connect_wrapper
1403-
1404-
1405-class FancyURLOpenerWithRedirectedPOST(urllib.FancyURLopener):
1406- """FancyURLopener does not redirect postdata when redirecting POSTs"""
1407- version = "Ubuntu One/Login (%s)" % ubuntuone_client_version
1408- def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
1409- """Actually perform a redirect"""
1410- # All the same as the original, except passing data, below
1411- if 'location' in headers:
1412- newurl = headers['location']
1413- elif 'uri' in headers:
1414- newurl = headers['uri']
1415- else:
1416- return
1417- fp.read()
1418- fp.close()
1419- # In case the server sent a relative URL, join with original:
1420- newurl = urllib.basejoin(self.type + ":" + url, newurl)
1421-
1422- # pass data if present when we redirect
1423- if data:
1424- return self.open(newurl, data)
1425- else:
1426- return self.open(newurl)
1427-
1428-class AuthorisationClient(object):
1429- """OAuth authorisation client."""
1430- def __init__(self, realm, request_token_url, user_authorisation_url,
1431- access_token_url, consumer_key, consumer_secret,
1432- callback_parent, callback_denied=None,
1433- callback_notoken=None, callback_error=None, do_login=True,
1434- keyring=gnomekeyring):
1435- """Create an `AuthorisationClient` instance.
1436-
1437- @param realm: the OAuth realm.
1438- @param request_token_url: the OAuth request token URL.
1439- @param user_authorisation_url: the OAuth user authorisation URL.
1440- @param access_token_url: the OAuth access token URL.
1441- @param consumer_key: the OAuth consumer key.
1442- @param consumer_secret: the OAuth consumer secret.
1443- @param callback_parent: a function in the includer to call with a token
1444-
1445- The preceding parameters are defined in sections 3 and 4.1 of the
1446- OAuth Core 1.0 specification. The following parameters are not:
1447-
1448- @param callback_denied: a function to call if no token is available
1449- @param do_login: whether to create a token if one is not cached
1450- @param keychain: the keyring object to use (defaults to gnomekeyring)
1451-
1452- """
1453- self.realm = realm
1454- self.request_token_url = request_token_url
1455- self.user_authorisation_url = user_authorisation_url
1456- self.access_token_url = access_token_url
1457- self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
1458- self.callback_parent = callback_parent
1459- self.callback_denied = callback_denied
1460- self.callback_notoken = callback_notoken
1461- self.callback_error = callback_error
1462- self.do_login = do_login
1463- self.request_token = None
1464- self.saved_acquire_details = (None, None, None)
1465- self.keyring = keyring
1466- logger.debug("auth.AuthorisationClient created with parameters "+ \
1467- "realm='%s', request_token_url='%s', user_authorisation_url='%s',"+\
1468- "access_token_url='%s', consumer_key='%s', callback_parent='%s'",
1469- realm, request_token_url, user_authorisation_url, access_token_url,
1470- consumer_key, callback_parent)
1471-
1472- def _get_keyring_items(self):
1473- """Raw interface to obtain keyring items."""
1474- return self.keyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET,
1475- {'ubuntuone-realm': self.realm,
1476- 'oauth-consumer-key':
1477- self.consumer.key})
1478-
1479- def _forward_error_callback(self, error):
1480- """Forward an error through callback_error()"""
1481- if self.callback_error:
1482- self.callback_error(str(error))
1483- else:
1484- raise error
1485-
1486- def get_access_token(self):
1487- """Get the access token from the keyring.
1488-
1489- If no token is available in the keyring, `NoAccessToken` is raised.
1490- """
1491- logger.debug("Trying to fetch the token from the keyring")
1492- try:
1493- items = self._get_keyring_items()
1494- except (gnomekeyring.NoMatchError,
1495- gnomekeyring.DeniedError):
1496- logger.debug("Access token was not in the keyring")
1497- raise NoAccessToken("No access token found.")
1498- logger.debug("Access token successfully found in the keyring")
1499- return oauth.OAuthToken.from_string(items[0].secret)
1500-
1501- def clear_token(self):
1502- """Clear any stored tokens from the keyring."""
1503- logger.debug("Searching keyring for existing tokens to delete.")
1504- try:
1505- items = self._get_keyring_items()
1506- except (gnomekeyring.NoMatchError,
1507- gnomekeyring.DeniedError):
1508- logger.debug("No preexisting tokens found")
1509- else:
1510- logger.debug("Deleting %s tokens from the keyring" % len(items))
1511- for item in items:
1512- try:
1513- self.keyring.item_delete_sync(None, item.item_id)
1514- except gnomekeyring.DeniedError:
1515- logger.debug("Permission denied deleting token")
1516-
1517- def store_token(self, access_token):
1518- """Store the given access token in the keyring.
1519-
1520- The keyring item is identified by the OAuth realm and consumer
1521- key to support multiple instances.
1522- """
1523- logger.debug("Trying to store the token in the keyring")
1524- try:
1525- item_id = self.keyring.item_create_sync(
1526- None,
1527- gnomekeyring.ITEM_GENERIC_SECRET,
1528- 'UbuntuOne token for %s' % self.realm,
1529- {'ubuntuone-realm': self.realm,
1530- 'oauth-consumer-key': self.consumer.key},
1531- access_token.to_string(),
1532- True)
1533- except gnomekeyring.DeniedError:
1534- logger.debug("Permission denied storing token")
1535- else:
1536- # set ACLs on the key for all apps listed in xdg BaseDir, but only
1537- # the root level one, not the user-level one
1538- logger.debug("Setting ACLs on the token in the keyring")
1539- set_all_key_acls(item_id=item_id)
1540-
1541- # keyring seems to take a while to actually apply the change
1542- # for when other people retrieve it, so sleep a bit.
1543- # this ought to get fixed.
1544- import time
1545- time.sleep(4)
1546-
1547- def have_access_token(self):
1548- """Returns true if an access token is available from the keyring."""
1549- try:
1550- self.get_access_token()
1551- except NoAccessToken:
1552- return False
1553- else:
1554- return True
1555-
1556- def make_token_request(self, oauth_request):
1557- """Perform the given `OAuthRequest` and return the associated token."""
1558- logger.debug("Making a token request.")
1559- # Note that we monkeypatched httplib above to handle invalid certs
1560- # Ways this urlopen can fail:
1561- # bad certificate
1562- # raises IOError, e.args[1] == SSLError, e.args[1].errno == 1
1563- # No such server
1564- # raises IOError, e.args[1] == SSLError, e.args[1].errno == -2
1565- try:
1566- opener = FancyURLOpenerWithRedirectedPOST()
1567- fp = opener.open(oauth_request.http_url, oauth_request.to_postdata())
1568- data = fp.read()
1569- except IOError, e:
1570- self._forward_error_callback(e)
1571- logger.exception("make_token_request failed when getting a token.")
1572- return
1573-
1574- # we deliberately trap anything that might go wrong when parsing the
1575- # token, because we do not want this to explicitly fail
1576- # pylint: disable-msg=W0702
1577- try:
1578- out_token = oauth.OAuthToken.from_string(data)
1579- logger.info("Token successfully requested")
1580- return out_token
1581- except:
1582- error = Exception(data)
1583- logger.exception("Token was not retrieved: data was '%s'", data)
1584- self._forward_error_callback(error)
1585-
1586- def open_in_browser(self, url):
1587- """Open the given URL in the user's web browser."""
1588- logger.debug("Opening '%s' in the browser", url)
1589- p = subprocess.Popen(["xdg-open", url], bufsize=4096,
1590- stderr=subprocess.PIPE)
1591- p.wait()
1592- if p.returncode != 0:
1593- errors = "".join(p.stderr.readlines())
1594- if errors != "":
1595- self._forward_error_callback(IOError(errors))
1596-
1597- def acquire_access_token_if_online(self, description=None, store=False):
1598- """Check to see if we are online before trying to acquire"""
1599- # Get NetworkManager state
1600- logger.debug("Checking whether we are online")
1601- try:
1602- nm = dbus.SystemBus().get_object('org.freedesktop.NetworkManager',
1603- '/org/freedesktop/NetworkManager',
1604- follow_name_owner_changes=True)
1605- except dbus.exceptions.DBusException:
1606- logger.warn("Unable to connect to NetworkManager. Trying anyway.")
1607- self.acquire_access_token(description, store)
1608- else:
1609- iface = dbus.Interface(nm, 'org.freedesktop.NetworkManager')
1610-
1611- def got_state(state):
1612- """Handler for when state() call succeeds."""
1613- if state == NM_STATE_CONNECTED:
1614- logger.debug("We are online")
1615- self.acquire_access_token(description, store)
1616- elif state == NM_STATE_CONNECTING:
1617- logger.debug("We are currently going online")
1618- # attach to NM's StateChanged signal
1619- signal_match = nm.connect_to_signal(
1620- signal_name="StateChanged",
1621- handler_function=self.connection_established,
1622- dbus_interface="org.freedesktop.NetworkManager")
1623- # stash the details so the handler_function can get at them
1624- self.saved_acquire_details = (signal_match, description,
1625- store)
1626- else:
1627- # NM is not connected: fail
1628- logger.debug("We are not online")
1629-
1630- def got_error(error):
1631- """Handler for D-Bus errors when calling state()."""
1632- if error.get_dbus_name() == \
1633- 'org.freedesktop.DBus.Error.ServiceUnknown':
1634- logger.debug("NetworkManager not available.")
1635- self.acquire_access_token(description, store)
1636- else:
1637- logger.error("Error contacting NetworkManager: %s" % \
1638- str(error))
1639-
1640-
1641- iface.state(reply_handler=got_state, error_handler=got_error)
1642-
1643- def connection_established(self, state):
1644- """NetworkManager's state has changed, and we're watching for
1645- a connection"""
1646- logger.debug("Online status has changed to %s" % state)
1647- if int(state) == NM_STATE_CONNECTED:
1648- signal_match, description, store = self.saved_acquire_details
1649- # disconnect the signal so we don't get called again
1650- signal_match.remove()
1651- # call the real acquire_access_token now it has a connection
1652- logger.debug("Correctly connected: now starting auth process")
1653- self.acquire_access_token(description, store)
1654- else:
1655- # connection changed but not to "connected", so keep waiting
1656- logger.debug("Not yet connected: continuing to wait")
1657-
1658- def acquire_access_token(self, description=None, store=False):
1659- """Create an OAuth access token authorised against the user."""
1660- signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
1661-
1662- # Create a request token ...
1663- logger.debug("Creating a request token to begin access request")
1664- parameters = {}
1665- if description:
1666- parameters['description'] = description
1667- # Add a nonce to the query so we know the callback (to our temp
1668- # webserver) came from us
1669- nonce = random.randint(1000000, 10000000)
1670-
1671- # start temporary webserver to receive browser response
1672- callback_url = self.get_temporary_httpd(nonce,
1673- self.retrieve_access_token, store)
1674-
1675- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
1676- callback=callback_url,
1677- http_url=self.request_token_url,
1678- oauth_consumer=self.consumer,
1679- parameters=parameters)
1680- oauth_request.sign_request(signature_method, self.consumer, None)
1681- logger.debug("Making token request")
1682- self.request_token = self.make_token_request(oauth_request)
1683-
1684- if self.request_token is None:
1685- msg = "acquire_access_token: request_token is None after " \
1686- "make_token_request. Stopping reactor and exiting."
1687- logger.error(msg)
1688- reactor.stop()
1689- sys.exit(1)
1690-
1691- # Request authorisation from the user
1692- oauth_request = oauth.OAuthRequest.from_token_and_callback(
1693- http_url=self.user_authorisation_url,
1694- token=self.request_token)
1695- nodename = os.uname()[1]
1696- if nodename:
1697- oauth_request.set_parameter("description", nodename)
1698- Thread(target=self.open_in_browser, name="authorization",
1699- args=(oauth_request.to_url(),)).start()
1700-
1701- def get_temporary_httpd(self, nonce, retrieve_function, store):
1702- "A separate class so it can be mocked in testing"
1703- logger.debug("Creating a listening temp web server")
1704- site = TemporaryTwistedWebServer(nonce=nonce,
1705- retrieve_function=retrieve_function, store_yes_no=store)
1706- temphttpd = server.Site(site)
1707- temphttpdport = reactor.listenTCP(0, temphttpd)
1708- callback_url = "http://localhost:%s/?nonce=%s" % (
1709- temphttpdport.getHost().port, nonce)
1710- site.set_port(temphttpdport)
1711- logger.debug("Webserver listening on port '%s'", temphttpdport)
1712- return callback_url
1713-
1714- def retrieve_access_token(self, store=False, verifier=None):
1715- """Retrieve the access token, once OAuth is done. This is a callback."""
1716- logger.debug("Access token callback from temp webserver")
1717- signature_method = oauth.OAuthSignatureMethod_PLAINTEXT()
1718- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
1719- http_url=self.access_token_url,
1720- oauth_consumer=self.consumer,
1721- token=self.request_token)
1722- oauth_request.set_parameter("oauth_verifier", verifier)
1723- oauth_request.sign_request(
1724- signature_method, self.consumer, self.request_token)
1725- logger.debug("Retrieving access token from OAuth")
1726- access_token = self.make_token_request(oauth_request)
1727- if not access_token:
1728- logger.error("Failed to get access token.")
1729- if self.callback_denied is not None:
1730- self.callback_denied()
1731- else:
1732- if store:
1733- logger.debug("Storing access token in keyring")
1734- self.store_token(access_token)
1735- logger.debug("Calling the callback_parent")
1736- self.callback_parent(access_token)
1737-
1738- def ensure_access_token(self, description=None):
1739- """Returns an access token, either from the keyring or newly acquired.
1740-
1741- If a new token is acquired, it will be stored in the keyring
1742- for future use.
1743- """
1744- try:
1745- access_token = self.get_access_token()
1746- self.callback_parent(access_token)
1747- except NoAccessToken:
1748- if self.do_login:
1749- self.acquire_access_token_if_online(description, store=True)
1750- else:
1751- if self.callback_notoken is not None:
1752- self.callback_notoken()
1753-
1754-
1755-class TemporaryTwistedWebServer(resource.Resource):
1756- """A temporary httpd for the oauth process to call back to"""
1757- isLeaf = True
1758- def __init__(self, nonce, store_yes_no, retrieve_function):
1759- """Initialize the temporary web server."""
1760- resource.Resource.__init__(self)
1761- self.nonce = nonce
1762- self.store_yes_no = store_yes_no
1763- self.retrieve_function = retrieve_function
1764- reactor.callLater(600, self.stop) # ten minutes
1765- self.port = None
1766- def set_port(self, port):
1767- """Save the Twisted port object so we can stop it later"""
1768- self.port = port
1769- def stop(self):
1770- """Stop the httpd"""
1771- logger.debug("Stopping temp webserver")
1772- self.port.stopListening()
1773- def render_GET(self, request):
1774- """Handle incoming web requests"""
1775- logger.debug("Incoming temp webserver hit received")
1776- nonce = request.args.get("nonce", [None])[0]
1777- url = request.args.get("return", ["https://one.ubuntu.com/"])[0]
1778- verifier = request.args.get("oauth_verifier", [None])[0]
1779- logger.debug("Got verifier %s" % verifier)
1780- if nonce and (str(nonce) == str(self.nonce) and verifier):
1781- self.retrieve_function(store=self.store_yes_no, verifier=verifier)
1782- reactor.callLater(3, self.stop)
1783- return """<!doctype html>
1784- <html><head><meta http-equiv="refresh"
1785- content="0;url=%(url)s">
1786- </head>
1787- <body>
1788- <p>You should now automatically <a
1789- href="%(url)s">return to %(url)s</a>.</p>
1790- </body>
1791- </html>
1792- """ % { 'url' : url }
1793- else:
1794- self.retrieve_function(store=self.store_yes_no, verifier=verifier)
1795- reactor.callLater(3, self.stop)
1796- request.setResponseCode(400)
1797- return """<!doctype html>
1798- <html><head><title>Error</title></head>
1799- <body>
1800- <h1>There was an error</h1>
1801- <p>The authentication process has not succeeded. This may be a
1802- temporary problem; please try again in a few minutes.</p>
1803- </body>
1804- </html>
1805- """
1806-
1807-
1808-
1809
1810=== removed file 'ubuntu_sso/config.py'
1811--- ubuntu_sso/config.py 2010-06-16 15:11:04 +0000
1812+++ ubuntu_sso/config.py 1970-01-01 00:00:00 +0000
1813@@ -1,47 +0,0 @@
1814-# ubuntu_sso.config - Configuration for OAuthDesktop
1815-#
1816-# Author: Stuart Langridge <stuart.langridge@canonical.com>
1817-#
1818-# Copyright 2009 Canonical Ltd.
1819-#
1820-# This program is free software: you can redistribute it and/or modify it
1821-# under the terms of the GNU General Public License version 3, as published
1822-# by the Free Software Foundation.
1823-#
1824-# This program is distributed in the hope that it will be useful, but
1825-# WITHOUT ANY WARRANTY; without even the implied warranties of
1826-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1827-# PURPOSE. See the GNU General Public License for more details.
1828-#
1829-# You should have received a copy of the GNU General Public License along
1830-# with this program. If not, see <http://www.gnu.org/licenses/>.
1831-"""Find the config file for ubuntu-oauth-config."""
1832-
1833-import os, ConfigParser
1834-from xdg.BaseDirectory import load_first_config
1835-
1836-def get_config(use_tmpconfig=True):
1837- """Return a ConfigParser object from a config file.
1838-
1839- Config file is looked for in the source tree first and then in the
1840- FreeDesktop BaseDirectory folders.
1841- """
1842- # if tmpconfig exists, then we are running out of the source tree
1843- tmpconfig = os.path.realpath(os.path.join(__file__,
1844- "../../../data/oauth_urls"))
1845- if os.path.isfile(tmpconfig) and use_tmpconfig:
1846- config_file = tmpconfig
1847- else:
1848- config_file = load_first_config('ubuntu-sso','oauth_urls')
1849-
1850- cfp = ConfigParser.ConfigParser()
1851- cfp.FILENAME = None
1852- if config_file is not None:
1853- try:
1854- cfp.read(config_file)
1855- cfp.FILENAME = config_file
1856- except ConfigParser.Error:
1857- cfp = ConfigParser.ConfigParser()
1858- cfp.FILENAME = None
1859- return cfp
1860-
1861
1862=== modified file 'ubuntu_sso/gui.py'
1863--- ubuntu_sso/gui.py 2010-08-27 22:16:40 +0000
1864+++ ubuntu_sso/gui.py 2010-09-09 14:47:44 +0000
1865@@ -35,20 +35,23 @@
1866 from dbus.mainloop.glib import DBusGMainLoop
1867
1868 from ubuntu_sso import DBUS_PATH, DBUS_BUS_NAME, DBUS_IFACE_USER_NAME
1869-from ubuntu_sso.logger import setupLogging
1870+from ubuntu_sso.logger import setup_logging
1871+
1872+
1873+# Instance of 'UbuntuSSOClientGUI' has no 'yyy' member
1874+# pylint: disable=E1101
1875
1876
1877 _ = gettext.gettext
1878 gettext.textdomain('ubuntu-sso-client')
1879
1880 DBusGMainLoop(set_as_default=True)
1881-logger = setupLogging('ubuntu_sso.gui')
1882+logger = setup_logging('ubuntu_sso.gui')
1883
1884
1885 NO_OP = lambda *args, **kwargs: None
1886 DEFAULT_WIDTH = 30
1887-# XXX: to be replaced by values from the theme
1888-# see LP: #616526
1889+# To be replaced by values from the theme (LP: #616526)
1890 HELP_TEXT_COLOR = gtk.gdk.Color("#bfbfbf")
1891 WARNING_TEXT_COLOR = gtk.gdk.Color("red")
1892
1893@@ -66,9 +69,9 @@
1894 (SIG_USER_CANCELATION, (gobject.TYPE_STRING,)),
1895 ]
1896
1897-for sig, args in SIGNAL_ARGUMENTS:
1898+for sig, sig_args in SIGNAL_ARGUMENTS:
1899 gobject.signal_new(sig, gtk.Window, gobject.SIGNAL_RUN_FIRST,
1900- gobject.TYPE_NONE, args)
1901+ gobject.TYPE_NONE, sig_args)
1902
1903
1904 def get_data_dir():
1905@@ -298,7 +301,6 @@
1906 self.window.set_icon_name('ubuntu-logo')
1907
1908 self.bus = dbus.SessionBus()
1909- self.bus.add_signal_receiver = self._log(self.bus.add_signal_receiver)
1910 obj = self.bus.get_object(bus_name=DBUS_BUS_NAME,
1911 object_path=DBUS_PATH,
1912 follow_name_owner_changes=True)
1913@@ -321,7 +323,7 @@
1914
1915 window_size = None
1916 if not login_only:
1917- window_size = (550, 600)
1918+ window_size = (550, 500)
1919 self._append_page(self._build_enter_details_page())
1920 self._append_page(self._build_tc_page())
1921 self._append_page(self._build_verify_email_page())
1922@@ -339,6 +341,32 @@
1923 for label in self.labels:
1924 label.set_size_request(*size_req)
1925
1926+ self._signals = {
1927+ 'CaptchaGenerated':
1928+ self._filter_by_app_name(self.on_captcha_generated),
1929+ 'CaptchaGenerationError':
1930+ self._filter_by_app_name(self.on_captcha_generation_error),
1931+ 'UserRegistered':
1932+ self._filter_by_app_name(self.on_user_registered),
1933+ 'UserRegistrationError':
1934+ self._filter_by_app_name(self.on_user_registration_error),
1935+ 'EmailValidated':
1936+ self._filter_by_app_name(self.on_email_validated),
1937+ 'EmailValidationError':
1938+ self._filter_by_app_name(self.on_email_validation_error),
1939+ 'LoggedIn':
1940+ self._filter_by_app_name(self.on_logged_in),
1941+ 'LoginError':
1942+ self._filter_by_app_name(self.on_login_error),
1943+ 'PasswordResetTokenSent':
1944+ self._filter_by_app_name(self.on_password_reset_token_sent),
1945+ 'PasswordResetError':
1946+ self._filter_by_app_name(self.on_password_reset_error),
1947+ 'PasswordChanged':
1948+ self._filter_by_app_name(self.on_password_changed),
1949+ 'PasswordChangeError':
1950+ self._filter_by_app_name(self.on_password_change_error),
1951+ }
1952 self._setup_signals()
1953 self._gtk_signal_log = []
1954
1955@@ -349,51 +377,55 @@
1956 # still do everything as a standalone window. Also,
1957 # window_foreign_new may return None breaking set_transient_for.
1958 try:
1959- r = gtk.gdk.window_foreign_new(window_id)
1960+ win = gtk.gdk.window_foreign_new(window_id)
1961 self.window.realize()
1962- self.window.window.set_transient_for(r)
1963- except:
1964+ self.window.window.set_transient_for(win)
1965+ except: # pylint: disable=W0702
1966 msg = 'UbuntuSSOClientGUI: failed set_transient_for win id %r'
1967 logger.exception(msg, window_id)
1968+
1969+ # Hidding unused widgets to save some space (LP #627440).
1970+ self.name_entry.hide()
1971+ self.yes_to_updates_checkbutton.hide()
1972+
1973 self.window.show()
1974
1975+ def _filter_by_app_name(self, f):
1976+ """Excecute the decorated function only for 'self.app_name'."""
1977+
1978+ @wraps(f)
1979+ def inner(app_name, *args, **kwargs):
1980+ """Execute 'f' only if 'app_name' matches 'self.app_name'."""
1981+ result = None
1982+ if app_name == self.app_name:
1983+ result = f(app_name, *args, **kwargs)
1984+ else:
1985+ logger.info('%s: ignoring call since received app_name '\
1986+ '"%s" (expected "%s")',
1987+ f.__name__, app_name, self.app_name)
1988+ return result
1989+
1990+ return inner
1991+
1992 def _setup_signals(self):
1993 """Bind signals to callbacks to be able to test the pages."""
1994- self._signals = [
1995- ('CaptchaGenerated', self.on_captcha_generated),
1996- ('CaptchaGenerationError', self.on_captcha_generation_error),
1997- ('UserRegistered', self.on_user_registered),
1998- ('UserRegistrationError', self.on_user_registration_error),
1999- ('EmailValidated', self.on_email_validated),
2000- ('EmailValidationError', self.on_email_validation_error),
2001- ('LoggedIn', self.on_logged_in),
2002- ('LoginError', self.on_login_error),
2003- ('PasswordResetTokenSent', self.on_password_reset_token_sent),
2004- ('PasswordResetError', self.on_password_reset_error),
2005- ('PasswordChanged', self.on_password_changed),
2006- ('PasswordChangeError', self.on_password_change_error),
2007- ]
2008- for signal, method in self._signals:
2009- self.bus.add_signal_receiver(method, signal_name=signal,
2010- dbus_interface=self.iface_name)
2011+ iface = self.iface_name
2012+ for signal, method in self._signals.iteritems():
2013+ actual = self._signals_receivers.get((iface, signal))
2014+ if actual is not None:
2015+ msg = 'Signal %r is already connected with %r at iface %r.'
2016+ logger.warning(msg, signal, actual, iface)
2017+
2018+ match = self.bus.add_signal_receiver(method, signal_name=signal,
2019+ dbus_interface=iface)
2020+ logger.info('Connecting signal %r with method %r at iface %r.' \
2021+ 'Match: %r', signal, method, iface, match)
2022+ self._signals_receivers[(iface, signal)] = method
2023
2024 def _debug(self, *args, **kwargs):
2025 """Do some debugging."""
2026 print args, kwargs
2027
2028- def _log(self, f):
2029- """Keep track of added signals to the internal bus."""
2030-
2031- @wraps(f)
2032- def inner(method, signal_name, dbus_interface):
2033- """Do the deed."""
2034- actual = self._signals_receivers.get((dbus_interface, signal_name))
2035- assert actual is None
2036- f(method, signal_name, dbus_interface)
2037- self._signals_receivers[(dbus_interface, signal_name)] = method
2038-
2039- return inner
2040-
2041 def _add_spinner_to_container(self, container, legend=None):
2042 """Add a spinner to 'container'."""
2043 spinner = gtk.Spinner()
2044@@ -467,6 +499,9 @@
2045 else:
2046 page.hide()
2047
2048+ if current_page.default_widget is not None:
2049+ current_page.default_widget.grab_default()
2050+
2051 def _generate_captcha(self):
2052 """Ask for a new captcha; update the ui to reflect the fact."""
2053 logger.info('Calling generate_captcha with filename path at %r',
2054@@ -496,6 +531,8 @@
2055 d = {'app_name': self.app_label}
2056 self.enter_details_vbox.header = self.JOIN_HEADER_LABEL % d
2057 self.enter_details_vbox.help_text = self.help_text
2058+ self.enter_details_vbox.default_widget = self.join_ok_button
2059+ self.join_ok_button.set_flags(gtk.CAN_DEFAULT)
2060
2061 self.enter_details_vbox.pack_start(self.name_entry, expand=False)
2062 self.enter_details_vbox.reorder_child(self.name_entry, 0)
2063@@ -538,16 +575,22 @@
2064 def _build_tc_page(self):
2065 """Build the Terms & Conditions page."""
2066 self.tc_browser_vbox.help_text = ''
2067+ self.tc_browser_vbox.default_widget = self.tc_back_button
2068+ self.tc_browser_vbox.default_widget.set_flags(gtk.CAN_DEFAULT)
2069 return self.tc_browser_vbox
2070
2071 def _build_processing_page(self):
2072 """Build the processing page with a spinner."""
2073+ self.processing_vbox.default_widget = None
2074 self._add_spinner_to_container(self.processing_vbox,
2075 legend=self.ONE_MOMENT_PLEASE)
2076 return self.processing_vbox
2077
2078 def _build_verify_email_page(self):
2079 """Build the verify email page."""
2080+ self.verify_email_vbox.default_widget = self.verify_token_button
2081+ self.verify_email_vbox.default_widget.set_flags(gtk.CAN_DEFAULT)
2082+
2083 cb = lambda w: self.verify_token_button.clicked()
2084 self.email_token_entry.connect('activate', cb)
2085 self.verify_email_vbox.pack_start(self.email_token_entry, expand=False)
2086@@ -557,6 +600,9 @@
2087
2088 def _build_success_page(self):
2089 """Build the success page."""
2090+ self.success_vbox.default_widget = self.success_close_button
2091+ self.success_vbox.default_widget.set_flags(gtk.CAN_DEFAULT)
2092+
2093 self.success_label.set_markup('<span size="x-large">%s</span>' %
2094 self.SUCCESS)
2095 return self.success_vbox
2096@@ -566,6 +612,8 @@
2097 d = {'app_name': self.app_label}
2098 self.login_vbox.header = self.LOGIN_HEADER_LABEL % d
2099 self.login_vbox.help_text = self.CONNECT_HELP_LABEL % d
2100+ self.login_vbox.default_widget = self.login_ok_button
2101+ self.login_vbox.default_widget.set_flags(gtk.CAN_DEFAULT)
2102
2103 self.login_details_vbox.pack_start(self.login_email_entry)
2104 self.login_details_vbox.reorder_child(self.login_email_entry, 0)
2105@@ -586,6 +634,9 @@
2106 self.request_password_token_vbox.header = self.RESET_PASSWORD
2107 text = self.REQUEST_PASSWORD_TOKEN_LABEL % {'app_name': self.app_label}
2108 self.request_password_token_vbox.help_text = text
2109+ btn = self.request_password_token_ok_button
2110+ btn.set_flags(gtk.CAN_DEFAULT)
2111+ self.request_password_token_vbox.default_widget = btn
2112
2113 entry = self.reset_email_entry
2114 self.request_password_token_details_vbox.pack_start(entry,
2115@@ -604,6 +655,9 @@
2116 """Build the login page."""
2117 self.set_new_password_vbox.header = self.RESET_PASSWORD
2118 self.set_new_password_vbox.help_text = self.SET_NEW_PASSWORD_LABEL
2119+ btn = self.set_new_password_ok_button
2120+ btn.set_flags(gtk.CAN_DEFAULT)
2121+ self.set_new_password_vbox.default_widget = btn
2122
2123 cb = lambda w: self.set_new_password_ok_button.clicked()
2124 for entry in (self.reset_code_entry,
2125@@ -672,6 +726,8 @@
2126 remove = self.bus.remove_signal_receiver
2127 for (iface, signal) in self._signals_receivers.keys():
2128 method = self._signals_receivers.pop((iface, signal))
2129+ logger.info('Removing signal %r with method %r at iface %r.',
2130+ signal, method, iface)
2131 remove(method, signal_name=signal, dbus_interface=iface)
2132
2133 # destroy main window
2134@@ -684,6 +740,8 @@
2135 signal = self._gtk_signal_log[-1][0]
2136 args = self._gtk_signal_log[-1][1:]
2137 self.window.emit(signal, *args)
2138+ else:
2139+ self.window.emit(SIG_USER_CANCELATION, self.app_name)
2140
2141 # call user defined callback
2142 if self.close_callback is not None:
2143@@ -704,10 +762,11 @@
2144
2145 error = False
2146
2147- name = self.name_entry.get_text()
2148- if not name:
2149- self.name_entry.set_warning(self.FIELD_REQUIRED)
2150- error = True
2151+ # Hidding unused widgets to save some space (LP #627440).
2152+ #name = self.name_entry.get_text()
2153+ #if not name:
2154+ # self.name_entry.set_warning(self.FIELD_REQUIRED)
2155+ # error = True
2156
2157 # check email
2158 email1 = self.email1_entry.get_text()
2159@@ -892,6 +951,9 @@
2160 def on_tc_browser_vbox_show(self, *args, **kwargs):
2161 """The T&C page is being shown."""
2162 browser = webkit.WebView()
2163+ settings = browser.get_settings()
2164+ settings.set_property("enable-plugins", False)
2165+ settings.set_property("enable-default-context-menu", False)
2166 browser.open(self.tc_uri)
2167 browser.show()
2168 self.tc_browser_window.add(browser)
2169
2170=== removed file 'ubuntu_sso/key_acls.py'
2171--- ubuntu_sso/key_acls.py 2010-06-16 15:11:04 +0000
2172+++ ubuntu_sso/key_acls.py 1970-01-01 00:00:00 +0000
2173@@ -1,157 +0,0 @@
2174-# ubuntu_sso.key_acls - OAuth ACL handling for keyring
2175-#
2176-# Author: Stuart Langridge <stuart.langridge@canonical.com>
2177-#
2178-# Copyright 2009 Canonical Ltd.
2179-#
2180-# This program is free software: you can redistribute it and/or modify it
2181-# under the terms of the GNU General Public License version 3, as published
2182-# by the Free Software Foundation.
2183-#
2184-# This program is distributed in the hope that it will be useful, but
2185-# WITHOUT ANY WARRANTY; without even the implied warranties of
2186-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2187-# PURPOSE. See the GNU General Public License for more details.
2188-#
2189-# You should have received a copy of the GNU General Public License along
2190-# with this program. If not, see <http://www.gnu.org/licenses/>.
2191-"""OAuth client authorisation code.
2192-
2193-This code finds all apps that have pre-registered themselves as wanting to
2194-access OAuth tokens from the Gnome keyring without the user having to approve
2195-that, and sets ACLs on relevant keys so they can do so.
2196-
2197-Apps pre-register themselves by dropping an ini file in
2198-/etc/xdg/ubuntuone/oauth_registration.d/ in which each section has keys
2199-realm, consumer_key, exe_path, application_name.
2200-"""
2201-
2202-import xdg.BaseDirectory, os, ConfigParser, gnomekeyring
2203-
2204-def get_privileged_config_folder(use_source_tree_folder=True):
2205- """Find the XDG config folder to use which is not the user's personal
2206- config (i.e., ~/.config) so that files in it are root-owned"""
2207- # First, check for folder (if we're running from the source tree)
2208- if use_source_tree_folder:
2209- source_tree_folder = os.path.join(
2210- os.path.split(__file__)[0],
2211- "../../data/oauth_registration.d")
2212- if os.path.isdir(source_tree_folder):
2213- return os.path.join(source_tree_folder, "..")
2214-
2215- # Otherwise, check for proper XDG folders
2216- privileged_folders = [x for x in
2217- xdg.BaseDirectory.load_config_paths('ubuntu-sso')
2218- if not x.startswith(xdg.BaseDirectory.xdg_config_home)]
2219- if privileged_folders:
2220- return privileged_folders[0]
2221- else:
2222- return None
2223-
2224-def get_acl_preregistrations(use_source_tree_folder=True):
2225- "Return a list of all config files in the pre-registration folder"
2226- config_folder = get_privileged_config_folder(use_source_tree_folder)
2227- if config_folder:
2228- conf_dir = os.path.join(config_folder, "oauth_registration.d")
2229- if os.path.isdir(conf_dir):
2230- return [os.path.join(conf_dir, x) for x in os.listdir(conf_dir)]
2231- return []
2232-
2233-def get_item_ids_for_realm(realm, consumer_key):
2234- "Find all keyring tokens for a specific realm/consumer_key"
2235- if realm == "http://localhost":
2236- # if realm (from the config file) is localhost, then the token
2237- # will have ubuntuone-realm == http://localhost:SOMETHING
2238- # so find all keys with this consumer_key, and pass on any
2239- # where ubuntuone-realm begins with http://localhost:
2240- try:
2241- items = gnomekeyring.find_items_sync(
2242- gnomekeyring.ITEM_GENERIC_SECRET,
2243- {'oauth-consumer-key': consumer_key})
2244- except (gnomekeyring.NoMatchError,
2245- gnomekeyring.DeniedError):
2246- return []
2247- items = [x.item_id for x in items if
2248- x.attributes.get("ubuntuone-realm", "").startswith("http://localhost:")]
2249- return items
2250- else:
2251- # realm was not localhost, so search for it explicitly
2252- try:
2253- items = gnomekeyring.find_items_sync(
2254- gnomekeyring.ITEM_GENERIC_SECRET,
2255- {'ubuntuone-realm': realm,
2256- 'oauth-consumer-key': consumer_key})
2257- except (gnomekeyring.NoMatchError,
2258- gnomekeyring.DeniedError):
2259- return []
2260- return [x.item_id for x in items]
2261-
2262-def set_single_acl(app_sets, specific_item_id=None):
2263- """Allow a specified set of apps to access a matching keyring
2264- token without prompts"""
2265- for realm, consumer_key, exe_path, application_name in app_sets:
2266- if specific_item_id is None:
2267- items = get_item_ids_for_realm(realm, consumer_key)
2268- else:
2269- # item_id specified
2270- items = [specific_item_id]
2271-
2272- # set an ACL on the key so the calling app can read it without
2273- # a prompt dialog
2274- for item_id in items:
2275- acl = gnomekeyring.item_get_acl_sync(None, item_id)
2276- new_acls = False
2277- real_exe_path = os.path.realpath(exe_path)
2278- for acl_item in acl:
2279- if acl_item.get_display_name() == application_name and \
2280- acl_item.get_path_name() == real_exe_path:
2281- # this ACL is already set
2282- break
2283- else:
2284- appref = gnomekeyring.ApplicationRef()
2285- ac = gnomekeyring.AccessControl(appref,
2286- gnomekeyring.ACCESS_READ |
2287- gnomekeyring.ACCESS_WRITE | gnomekeyring.ACCESS_REMOVE)
2288- ac.set_display_name(application_name)
2289- ac.set_path_name(real_exe_path)
2290- acl.append(ac)
2291- new_acls = True
2292- if new_acls:
2293- gnomekeyring.item_set_acl_sync(None, item_id, acl)
2294-
2295-def set_all_key_acls(item_id=None, use_source_tree_folder=True):
2296- """For each file in the config folder, get the (realm, key) pair that
2297- the program therein is interested in and register the program as able
2298- to access those keys by setting an ACL on them."""
2299- for config_file in get_acl_preregistrations(use_source_tree_folder):
2300- cfp = ConfigParser.ConfigParser()
2301- try:
2302- cfp.read(config_file)
2303- except ConfigParser.Error:
2304- continue
2305-
2306- app_sets = []
2307-
2308- for section in cfp.sections():
2309- try:
2310- realm = cfp.get(section, "realm")
2311- except ConfigParser.NoOptionError:
2312- realm = None
2313- try:
2314- consumer_key = cfp.get(section, "consumer_key")
2315- except ConfigParser.NoOptionError:
2316- consumer_key = None
2317- try:
2318- exe_path = cfp.get(section, "exe_path")
2319- except ConfigParser.NoOptionError:
2320- exe_path = None
2321- try:
2322- application_name = cfp.get(section, "application_name")
2323- except ConfigParser.NoOptionError:
2324- application_name = None
2325- if realm and consumer_key and exe_path and application_name:
2326- app_sets.append((realm, consumer_key, exe_path,
2327- application_name))
2328- if app_sets:
2329- set_single_acl(app_sets, specific_item_id=item_id)
2330-
2331
2332=== modified file 'ubuntu_sso/keyring.py'
2333--- ubuntu_sso/keyring.py 2010-08-27 22:16:40 +0000
2334+++ ubuntu_sso/keyring.py 2010-09-09 14:47:44 +0000
2335@@ -25,8 +25,12 @@
2336
2337 import gnomekeyring
2338
2339-from ubuntu_sso.logger import setupLogging
2340-logger = setupLogging("ubuntu_sso.main")
2341+from ubuntu_sso.logger import setup_logging
2342+
2343+
2344+logger = setup_logging("ubuntu_sso.main")
2345+TOKEN_SEPARATOR = ' @ '
2346+SEPARATOR_REPLACEMENT = ' AT '
2347
2348 U1_APP_NAME = "Ubuntu One"
2349 U1_KEY_NAME = "UbuntuOne token for https://ubuntuone.com"
2350@@ -36,14 +40,22 @@
2351 }
2352
2353
2354-def get_token_name(app_name):
2355- """Build the token name."""
2356+def get_old_token_name(app_name):
2357+ """Build the token name (old style)."""
2358 quoted_app_name = urllib.quote(app_name)
2359 computer_name = socket.gethostname()
2360 quoted_computer_name = urllib.quote(computer_name)
2361 return "%s - %s" % (quoted_app_name, quoted_computer_name)
2362
2363
2364+def get_token_name(app_name):
2365+ """Build the token name."""
2366+ computer_name = socket.gethostname()
2367+ computer_name = computer_name.replace(TOKEN_SEPARATOR,
2368+ SEPARATOR_REPLACEMENT)
2369+ return TOKEN_SEPARATOR.join((app_name, computer_name)).encode('utf-8')
2370+
2371+
2372 class Keyring(object):
2373 """A Keyring for a given application name."""
2374 KEYRING_NAME = "login"
2375@@ -61,12 +73,14 @@
2376 if not name in keyring_names:
2377 gnomekeyring.create_sync(name)
2378
2379- def _find_keyring_item(self):
2380+ def _find_keyring_item(self, attr=None):
2381 """Return the keyring item or None if not found."""
2382+ if attr is None:
2383+ attr = self._get_keyring_attr()
2384 try:
2385 items = gnomekeyring.find_items_sync(
2386 gnomekeyring.ITEM_GENERIC_SECRET,
2387- self._get_keyring_attr())
2388+ attr)
2389 except gnomekeyring.NoMatchError:
2390 # if no items found, return None
2391 return None
2392@@ -98,10 +112,24 @@
2393 gnomekeyring.ITEM_GENERIC_SECRET, self.app_name,
2394 self._get_keyring_attr(), secret, True)
2395
2396+ def _migrate_old_token_name(self):
2397+ """Migrate credentials with old name, store them with new name."""
2398+ attr = self._get_keyring_attr()
2399+ attr['token-name'] = get_old_token_name(self.app_name)
2400+ item = self._find_keyring_item(attr=attr)
2401+ if item is not None:
2402+ self.set_ubuntusso_attr(dict(urlparse.parse_qsl(item.secret)))
2403+ gnomekeyring.item_delete_sync(item.keyring, item.item_id)
2404+
2405+ return self._find_keyring_item()
2406+
2407 def get_ubuntusso_attr(self):
2408 """Return the secret of the SSO item in a dictionary."""
2409 # If we have no attributes, return None
2410 item = self._find_keyring_item()
2411+ if item is None:
2412+ item = self._migrate_old_token_name()
2413+
2414 if item is not None:
2415 return dict(urlparse.parse_qsl(item.secret))
2416 else:
2417@@ -119,6 +147,7 @@
2418
2419
2420 class UbuntuOneOAuthKeyring(Keyring):
2421+ """A particular Keyring for Ubuntu One."""
2422
2423 def _get_keyring_attr(self):
2424 """Build the keyring attributes for this credentials."""
2425@@ -144,6 +173,8 @@
2426
2427
2428 if __name__ == "__main__":
2429+ # pylint: disable=C0103
2430+
2431 kr1 = Keyring("Test key 1")
2432 kr2 = Keyring("Test key 2")
2433
2434
2435=== modified file 'ubuntu_sso/logger.py'
2436--- ubuntu_sso/logger.py 2010-08-11 17:03:11 +0000
2437+++ ubuntu_sso/logger.py 2010-09-09 14:47:44 +0000
2438@@ -1,6 +1,7 @@
2439 # ubuntu_sso.logger - logging miscellany
2440 #
2441 # Author: Stuart Langridge <stuart.langridge@canonical.com>
2442+# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
2443 #
2444 # Copyright 2009 Canonical Ltd.
2445 #
2446@@ -26,34 +27,35 @@
2447
2448 from logging.handlers import RotatingFileHandler
2449
2450-home = xdg.BaseDirectory.xdg_cache_home
2451-LOGFOLDER = os.path.join(home, 'sso')
2452+LOGFOLDER = os.path.join(xdg.BaseDirectory.xdg_cache_home, 'sso')
2453 # create log folder if it doesn't exists
2454 if not os.path.exists(LOGFOLDER):
2455 os.makedirs(LOGFOLDER)
2456
2457 LOGFILENAME = os.path.join(LOGFOLDER, 'oauth-login.log')
2458-
2459-# Only log this level and above
2460-LOG_LEVEL = logging.DEBUG # logging.INFO
2461-
2462-root_formatter = logging.Formatter(
2463- fmt="%(asctime)s:%(msecs)s %(name)s %(message)s")
2464-root_handler = RotatingFileHandler(LOGFILENAME, maxBytes=1048576,
2465- backupCount=1)
2466-root_handler.setLevel(LOG_LEVEL)
2467-root_handler.setFormatter(root_formatter)
2468-
2469-
2470-def setupLogging(log_domain):
2471+FMT = "%(asctime)s:%(msecs)s - %(name)s - %(levelname)s - %(message)s"
2472+
2473+if os.environ.get('DEBUG'):
2474+ LOG_LEVEL = logging.DEBUG
2475+else:
2476+ # Only log this level and above
2477+ LOG_LEVEL = logging.INFO
2478+
2479+
2480+def setup_logging(log_domain):
2481 """Create basic logger to set filename"""
2482+ root_formatter = logging.Formatter(fmt=FMT)
2483+ root_handler = RotatingFileHandler(LOGFILENAME, maxBytes=1048576,
2484+ backupCount=5)
2485+ root_handler.setLevel(LOG_LEVEL)
2486+ root_handler.setFormatter(root_formatter)
2487+
2488 logger = logging.getLogger(log_domain)
2489 logger.propagate = False
2490 logger.setLevel(LOG_LEVEL)
2491 logger.addHandler(root_handler)
2492 if os.environ.get('DEBUG'):
2493 debug_handler = logging.StreamHandler(sys.stderr)
2494- debug_handler.setLevel(logging.DEBUG)
2495 logger.addHandler(debug_handler)
2496
2497 return logger
2498
2499=== modified file 'ubuntu_sso/main.py'
2500--- ubuntu_sso/main.py 2010-08-27 22:16:40 +0000
2501+++ ubuntu_sso/main.py 2010-09-09 14:47:44 +0000
2502@@ -1,8 +1,5 @@
2503-#!/usr/bin/python
2504-
2505 # ubuntu_sso.main - main login handling interface
2506 #
2507-# Author: Stuart Langridge <stuart.langridge@canonical.com>
2508 # Author: Natalia Bidart <natalia.bidart@canonical.com>
2509 # Author: Alejandro J. Cura <alecu@canonical.com>
2510 #
2511@@ -19,43 +16,45 @@
2512 #
2513 # You should have received a copy of the GNU General Public License along
2514 # with this program. If not, see <http://www.gnu.org/licenses/>.
2515-"""OAuth login handler.
2516-
2517-A command-line utility which accepts requests for OAuth login over D-Bus,
2518-handles the OAuth process (including adding the OAuth access token to the
2519-gnome keyring), and then alerts the calling app (and others) with a D-Bus
2520-signal so they can retrieve the new token.
2521+"""Single Sign On login handler.
2522+
2523+An utility which accepts requests for Ubuntu Single Sign On login over D-Bus.
2524+
2525+The OAuth process is handled, including adding the OAuth access token to the
2526+gnome keyring.
2527+
2528 """
2529
2530+import os
2531 import re
2532-import time
2533 import threading
2534 import traceback
2535 import urllib2
2536-import urlparse
2537
2538 import dbus.service
2539 import gobject
2540
2541-from dbus.mainloop.glib import DBusGMainLoop
2542+# Unable to import 'lazr.restfulclient.*'
2543+# pylint: disable=F0401
2544 from lazr.restfulclient.authorize import BasicHttpAuthorizer
2545 from lazr.restfulclient.authorize.oauth import OAuthAuthorizer
2546 from lazr.restfulclient.errors import HTTPError
2547 from lazr.restfulclient.resource import ServiceRoot
2548+# pylint: enable=F0401
2549 from oauth import oauth
2550
2551-from ubuntu_sso import (DBUS_IFACE_AUTH_NAME, DBUS_IFACE_USER_NAME,
2552- DBUS_IFACE_CRED_NAME, DBUS_CRED_PATH, DBUS_BUS_NAME, gui)
2553-from ubuntu_sso.config import get_config
2554+from ubuntu_sso import DBUS_IFACE_USER_NAME, DBUS_IFACE_CRED_NAME, gui
2555 from ubuntu_sso.keyring import Keyring, get_token_name, U1_APP_NAME
2556-from ubuntu_sso.logger import setupLogging
2557-logger = setupLogging("ubuntu_sso.main")
2558-
2559-DBusGMainLoop(set_as_default=True)
2560+from ubuntu_sso.logger import setup_logging
2561+
2562+
2563 # Disable the invalid name warning, as we have a lot of DBus style names
2564-# pylint: disable-msg=C0103
2565-
2566-PING_URL = "http://edge.one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
2567+# pylint: disable=C0103
2568+
2569+
2570+logger = setup_logging("ubuntu_sso.main")
2571+PING_URL = "https://edge.one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
2572+SERVICE_URL = "https://login.ubuntu.com/api/1.0"
2573
2574
2575 class NoDefaultConfigError(Exception):
2576@@ -99,16 +98,15 @@
2577
2578 def keyring_store_credentials(app_name, credentials):
2579 """Store the credentials in the keyring."""
2580- logger.debug('keyring_store_credentials: app_name %r.', app_name)
2581+ logger.info('keyring_store_credentials: app_name "%s".', app_name)
2582 Keyring(app_name).set_ubuntusso_attr(credentials)
2583
2584
2585 def keyring_get_credentials(app_name):
2586 """Get the credentials from the keyring or None if not there."""
2587- logger.debug('keyring_get_credentials: app_name %r', app_name)
2588 creds = Keyring(app_name).get_ubuntusso_attr()
2589- logger.debug('keyring_get_credentials: Keyring returned credentials? %r',
2590- creds is not None)
2591+ logger.info('keyring_get_credentials: app_name "%s", resulting credentials'
2592+ ' is not None? %r', app_name, creds is not None)
2593 return creds
2594
2595
2596@@ -122,11 +120,10 @@
2597 else:
2598 self.sso_service_class = sso_service_class
2599
2600- self.service_url = 'https://login.ubuntu.com/api/1.0'
2601+ self.service_url = os.environ.get('USSOC_SERVICE_URL', SERVICE_URL)
2602
2603 def _valid_email(self, email):
2604 """Validate the given email."""
2605- # XXX: to be improved as per django validation
2606 return email is not None and '@' in email
2607
2608 def _valid_password(self, password):
2609@@ -294,7 +291,7 @@
2610 """The part that runs inside the thread."""
2611 try:
2612 result_cb(app_name, f())
2613- except Exception, e:
2614+ except Exception, e: # pylint: disable=W0703
2615 msg = "Exception while running DBus blocking code in a thread:"
2616 logger.exception(msg)
2617 error_cb(app_name, except_to_errdict(e))
2618@@ -304,6 +301,9 @@
2619 class SSOLogin(dbus.service.Object):
2620 """Login thru the Single Sign On service."""
2621
2622+ # Operator not preceded by a space (fails with dbus decorators)
2623+ # pylint: disable=C0322
2624+
2625 def __init__(self, bus_name,
2626 sso_login_processor_class=SSOLoginProcessor,
2627 sso_service_class=None):
2628@@ -322,10 +322,14 @@
2629 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
2630 def CaptchaGenerated(self, app_name, result):
2631 """Signal thrown after the captcha is generated."""
2632+ logger.debug('SSOLogin: emitting CaptchaGenerated with app_name "%s" '
2633+ 'and result %r', app_name, result)
2634
2635 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
2636 def CaptchaGenerationError(self, app_name, error):
2637 """Signal thrown when there's a problem generating the captcha."""
2638+ logger.debug('SSOLogin: emitting CaptchaGenerationError with '
2639+ 'app_name "%s" and error %r', app_name, error)
2640
2641 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
2642 in_signature='ss')
2643@@ -341,10 +345,14 @@
2644 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
2645 def UserRegistered(self, app_name, result):
2646 """Signal thrown when the user is registered."""
2647+ logger.debug('SSOLogin: emitting UserRegistered with app_name "%s" '
2648+ 'and result %r', app_name, result)
2649
2650 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
2651 def UserRegistrationError(self, app_name, error):
2652 """Signal thrown when there's a problem registering the user."""
2653+ logger.debug('SSOLogin: emitting UserRegistrationError with '
2654+ 'app_name "%s" and error %r', app_name, error)
2655
2656 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
2657 in_signature='sssss')
2658@@ -361,10 +369,14 @@
2659 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
2660 def LoggedIn(self, app_name, result):
2661 """Signal thrown when the user is logged in."""
2662+ logger.debug('SSOLogin: emitting LoggedIn with app_name "%s" '
2663+ 'and result %r', app_name, result)
2664
2665 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
2666 def LoginError(self, app_name, error):
2667 """Signal thrown when there is a problem in the login."""
2668+ logger.debug('SSOLogin: emitting LoginError with '
2669+ 'app_name "%s" and error %r', app_name, error)
2670
2671 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
2672 in_signature='sss')
2673@@ -387,10 +399,14 @@
2674 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
2675 def EmailValidated(self, app_name, result):
2676 """Signal thrown after the email is validated."""
2677+ logger.debug('SSOLogin: emitting EmailValidated with app_name "%s" '
2678+ 'and result %r', app_name, result)
2679
2680 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
2681 def EmailValidationError(self, app_name, error):
2682 """Signal thrown when there's a problem validating the email."""
2683+ logger.debug('SSOLogin: emitting EmailValidationError with '
2684+ 'app_name "%s" and error %r', app_name, error)
2685
2686 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
2687 in_signature='ssss')
2688@@ -407,12 +423,16 @@
2689
2690 # request_password_reset_token signals
2691 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
2692- def PasswordResetTokenSent(self, app_name, email):
2693+ def PasswordResetTokenSent(self, app_name, result):
2694 """Signal thrown when the token is succesfully sent."""
2695+ logger.debug('SSOLogin: emitting PasswordResetTokenSent with app_name '
2696+ '"%s" and result %r', app_name, result)
2697
2698 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
2699 def PasswordResetError(self, app_name, error):
2700 """Signal thrown when there's a problem sending the token."""
2701+ logger.debug('SSOLogin: emitting PasswordResetError with '
2702+ 'app_name "%s" and error %r', app_name, error)
2703
2704 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
2705 in_signature='ss')
2706@@ -426,12 +446,16 @@
2707
2708 # set_new_password signals
2709 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
2710- def PasswordChanged(self, app_name, email):
2711+ def PasswordChanged(self, app_name, result):
2712 """Signal thrown when the token is succesfully sent."""
2713+ logger.debug('SSOLogin: emitting PasswordChanged with app_name "%s" '
2714+ 'and result %r', app_name, result)
2715
2716 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="sa{ss}")
2717 def PasswordChangeError(self, app_name, error):
2718 """Signal thrown when there's a problem sending the token."""
2719+ logger.debug('SSOLogin: emitting PasswordChangeError with '
2720+ 'app_name "%s" and error %r', app_name, error)
2721
2722 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
2723 in_signature='ssss')
2724@@ -447,23 +471,38 @@
2725 class SSOCredentials(dbus.service.Object):
2726 """DBus object that gets credentials, and login/registers if needed."""
2727
2728+ # Operator not preceded by a space (fails with dbus decorators)
2729+ # pylint: disable=C0322
2730+
2731+ def __init__(self, *args, **kwargs):
2732+ dbus.service.Object.__init__(self, *args, **kwargs)
2733+ self.ping_url = os.environ.get('USSOC_PING_URL', PING_URL)
2734+
2735 @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="s")
2736 def AuthorizationDenied(self, app_name):
2737 """Signal thrown when the user denies the authorization."""
2738+ logger.info('SSOLogin: emitting AuthorizationDenied with app_name '
2739+ '"%s"', app_name)
2740
2741 @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sa{ss}")
2742 def CredentialsFound(self, app_name, credentials):
2743 """Signal thrown when the credentials are found."""
2744+ logger.info('SSOLogin: emitting CredentialsFound with app_name "%s"',
2745+ app_name)
2746
2747 @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sss")
2748 def CredentialsError(self, app_name, error_message, detailed_error):
2749 """Signal thrown when there is a problem finding the credentials."""
2750+ logger.debug('SSOCredentials: emitting CredentialsError with app_name '
2751+ '"%s" and error_message %r', app_name, error_message)
2752
2753 @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME,
2754 in_signature="s", out_signature="a{ss}")
2755 def find_credentials(self, app_name):
2756 """Get the credentials from the keyring or '' if not there."""
2757 token = keyring_get_credentials(app_name)
2758+ logger.info('find_credentials: app_name "%s", result is {}? %s',
2759+ app_name, token is None)
2760 if token is None:
2761 return {}
2762 else:
2763@@ -476,7 +515,7 @@
2764 creds = keyring_get_credentials(app_name)
2765 self._ping_url(app_name, email, creds)
2766 self.CredentialsFound(app_name, creds)
2767- except Exception:
2768+ except: # pylint: disable=W0702
2769 msg = "Problem getting the credentials from the keyring."
2770 logger.exception(msg)
2771 self.CredentialsError(app_name, msg, traceback.format_exc())
2772@@ -493,9 +532,9 @@
2773
2774 def _ping_url(self, app_name, email, credentials):
2775 """Ping the given url."""
2776- logger.info('Maybe pinging url %s for app_name %r', PING_URL, app_name)
2777+ logger.info('Maybe pinging server for app_name "%s"', app_name)
2778 if app_name == U1_APP_NAME:
2779- url = PING_URL + email
2780+ url = self.ping_url + email
2781 consumer = oauth.OAuthConsumer(credentials['consumer_key'],
2782 credentials['consumer_secret'])
2783 token = oauth.OAuthToken(credentials['token'],
2784@@ -506,8 +545,8 @@
2785 oauth_req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
2786 consumer, token)
2787 request = urllib2.Request(url, headers=oauth_req.to_header())
2788- logger.debug('Opening the ping url with urllib2.urlopen. ' \
2789- 'Request to: %s', request.get_full_url())
2790+ logger.debug('Opening the ping url %s with urllib2.urlopen. ' \
2791+ 'Request to: %s', PING_URL, request.get_full_url())
2792 response = urllib2.urlopen(request)
2793 logger.debug('Url opened. Response: %s.', response.code)
2794 return response.code
2795@@ -525,7 +564,7 @@
2796 gui_app.connect(gui.SIG_REGISTRATION_FAILED, self._login_error_cb)
2797 gui_app.connect(gui.SIG_USER_CANCELATION,
2798 self._login_auth_denied_cb)
2799- except Exception:
2800+ except: # pylint: disable=W0702
2801 msg = '_show_login_or_register_ui failed when calling ' \
2802 'gui.UbuntuSSOClientGUI(%r, %r, %r, %r, %r)'
2803 logger.exception(msg, app_name, tc_url, help_text,
2804@@ -556,7 +595,7 @@
2805 help_text, window_id)
2806 else:
2807 self.CredentialsFound(app_name, token)
2808- except Exception:
2809+ except: # pylint: disable=W0702
2810 msg = "Problem getting the credentials from the keyring."
2811 logger.exception(msg)
2812 self.CredentialsError(app_name, msg, traceback.format_exc())
2813@@ -586,7 +625,7 @@
2814 help_text, window_id)
2815 else:
2816 self.CredentialsFound(app_name, token)
2817- except Exception:
2818+ except: # pylint: disable=W0702
2819 msg = "Problem getting the credentials from the keyring."
2820 logger.exception(msg)
2821 self.CredentialsError(app_name, msg, traceback.format_exc())
2822@@ -601,254 +640,7 @@
2823 try:
2824 creds = Keyring(app_name)
2825 creds.delete_ubuntusso_attr()
2826- except Exception:
2827+ except: # pylint: disable=W0702
2828 logger.exception(
2829 "problem removing credentials from keyring for %s",
2830 app_name)
2831-
2832-
2833-class LoginProcessor:
2834- """Actually do the work of processing passed parameters."""
2835-
2836- def __init__(self, dbus_object):
2837- """Initialize the login processor."""
2838- logger.debug("Creating a LoginProcessor")
2839- self.note1 = None
2840- self.realm = None
2841- self.consumer_key = None
2842- self.dbus_object = dbus_object
2843- logger.debug("Getting configuration")
2844- self.config = get_config()
2845-
2846- def login(self, realm, consumer_key, do_login=True):
2847- """Initiate an OAuth login"""
2848- logger.debug("Initiating OAuth login in LoginProcessor")
2849- self.realm = str(realm) # because they are dbus.Strings, not str
2850- self.consumer_key = str(consumer_key)
2851-
2852- logger.debug("Obtaining OAuth urls")
2853- (request_token_url, user_authorisation_url,
2854- access_token_url, consumer_secret) = self.get_config_urls(realm)
2855- logger.debug("OAuth URLs are: request='%s', userauth='%s', " + \
2856- "access='%s', secret='%s'", request_token_url,
2857- user_authorisation_url, access_token_url, consumer_secret)
2858-
2859- from ubuntu_sso.auth import AuthorisationClient
2860- client = AuthorisationClient(self.realm,
2861- request_token_url,
2862- user_authorisation_url,
2863- access_token_url, self.consumer_key,
2864- consumer_secret,
2865- callback_parent=self.got_token,
2866- callback_denied=self.got_denial,
2867- callback_notoken=self.got_no_token,
2868- callback_error=self.got_error,
2869- do_login=do_login)
2870-
2871- logger.debug("Calling auth.client.ensure_access_token in thread")
2872- gobject.timeout_add_seconds(1, client.ensure_access_token)
2873-
2874- def clear_token(self, realm, consumer_key):
2875- """Remove the currently stored OAuth token from the keyring."""
2876- self.realm = str(realm)
2877- self.consumer_key = str(consumer_key)
2878- (request_token_url, user_authorisation_url,
2879- access_token_url, consumer_secret) = self.get_config_urls(self.realm)
2880- from ubuntu_sso.auth import AuthorisationClient
2881- client = AuthorisationClient(self.realm,
2882- request_token_url,
2883- user_authorisation_url,
2884- access_token_url,
2885- self.consumer_key, consumer_secret,
2886- callback_parent=self.got_token,
2887- callback_denied=self.got_denial,
2888- callback_notoken=self.got_no_token,
2889- callback_error=self.got_error)
2890- gobject.timeout_add_seconds(1, client.clear_token)
2891-
2892- def error_handler(self, failure):
2893- """Deal with errors returned from auth process"""
2894- logger.debug("Error returned from auth process")
2895- self.dbus_object.currently_authing = False # not block future requests
2896-
2897- def get_config_urls(self, realm):
2898- """Look up the URLs to use in the config file"""
2899- logger.debug("Fetching config URLs for realm='%s'", realm)
2900- if self.config.has_section(realm):
2901- logger.debug("Realm '%s' is in config", realm)
2902- request_token_url = self.__get_url(realm, "request_token_url")
2903- user_authorisation_url = self.__get_url(realm,
2904- "user_authorisation_url")
2905- access_token_url = self.__get_url(realm, "access_token_url")
2906- consumer_secret = self.__get_option(realm, "consumer_secret")
2907- elif realm.startswith("http://localhost") and \
2908- self.config.has_section("http://localhost"):
2909- logger.debug("Realm is localhost and is in config")
2910- request_token_url = self.__get_url("http://localhost",
2911- "request_token_url", realm)
2912- user_authorisation_url = self.__get_url("http://localhost",
2913- "user_authorisation_url", realm)
2914- access_token_url = self.__get_url("http://localhost",
2915- "access_token_url", realm)
2916- consumer_secret = self.__get_option("http://localhost",
2917- "consumer_secret")
2918- elif self.is_valid_url(realm):
2919- logger.debug("Realm '%s' is not in config", realm)
2920- request_token_url = self.__get_url("default",
2921- "request_token_url", realm)
2922- user_authorisation_url = self.__get_url("default",
2923- "user_authorisation_url", realm)
2924- access_token_url = self.__get_url("default",
2925- "access_token_url", realm)
2926- consumer_secret = self.__get_option(realm, "consumer_secret")
2927- else:
2928- logger.debug("Realm '%s' is a bad realm", realm)
2929- raise BadRealmError
2930- return (request_token_url, user_authorisation_url,
2931- access_token_url, consumer_secret)
2932-
2933- def is_valid_url(self, url):
2934- """Simple check for URL validity"""
2935- # pylint: disable-msg=W0612
2936- scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
2937- if scheme and netloc:
2938- return True
2939- else:
2940- return False
2941-
2942- def got_token(self, access_token):
2943- """Callback function when access token has been retrieved"""
2944- logger.debug("Token retrieved, calling NewCredentials function")
2945- self.dbus_object.NewCredentials(self.realm, self.consumer_key)
2946-
2947- def got_denial(self):
2948- """Callback function when request token has been denied"""
2949- self.dbus_object.AuthorizationDenied()
2950-
2951- def got_no_token(self):
2952- """Callback function when access token is not in keyring."""
2953- self.dbus_object.NoCredentials()
2954-
2955- def got_error(self, message):
2956- """Callback function to emit an error message over DBus."""
2957- self.dbus_object.OAuthError(message)
2958-
2959- def __get_url(self, realm, option, actual_realm=None):
2960- """Construct a full URL from realm and a URLpath for that realm in
2961- the config file."""
2962- if actual_realm:
2963- realm_to_use = actual_realm
2964- else:
2965- realm_to_use = realm
2966- urlstub = self.__get_option(realm, option)
2967- return urlparse.urljoin(realm_to_use, urlstub)
2968-
2969- def __get_option(self, realm, option):
2970- """Return a specific option for that realm in
2971- the config file. If the realm does not exist in the config file,
2972- fall back to the [default] section."""
2973- if self.config.has_section(realm) and \
2974- self.config.has_option(realm, option):
2975- urlstub = self.config.get(realm, option)
2976- return urlstub
2977-
2978- # either the realm exists and this url does not, or
2979- # the realm doesn't exist; either way, fall back to [default] section
2980- urlstub = self.config.get("default", option, None)
2981- if urlstub is not None:
2982- return urlstub
2983-
2984- # this url does not exist in default section either
2985- # this shouldn't happen
2986- raise NoDefaultConfigError("No default configuration for %s" % option)
2987-
2988-
2989-class Login(dbus.service.Object):
2990- """Object which listens for D-Bus OAuth requests."""
2991-
2992- def __init__(self, bus_name):
2993- """Initiate the Login object."""
2994- dbus.service.Object.__init__(self, object_path="/", bus_name=bus_name)
2995- self.processor = LoginProcessor(self)
2996- self.currently_authing = False
2997- logger.debug("Login D-Bus service starting up")
2998-
2999- @dbus.service.method(dbus_interface=DBUS_IFACE_AUTH_NAME,
3000- in_signature='ss', out_signature='')
3001- def login(self, realm, consumer_key):
3002- """D-Bus method, to initiate an OAuth login."""
3003- logger.debug("login() D-Bus message received with realm='%s', " +
3004- "consumer_key='%s'", realm, consumer_key)
3005- if self.currently_authing:
3006- logger.debug("Currently in the middle of OAuth: rejecting this")
3007- return
3008- self.currently_authing = True
3009- self.processor.login(realm, consumer_key)
3010-
3011- @dbus.service.method(dbus_interface=DBUS_IFACE_AUTH_NAME,
3012- in_signature='ssb', out_signature='')
3013- def maybe_login(self, realm, consumer_key, do_login):
3014- """D-Bus method, to maybe initiate an OAuth login."""
3015- logger.debug("maybe_login() D-Bus message received with realm='%s', " +
3016- "consumer_key='%s'", realm, consumer_key)
3017- if self.currently_authing:
3018- logger.debug("Currently in the middle of OAuth: rejecting this")
3019- return
3020- self.currently_authing = True
3021- self.processor.login(realm, consumer_key, do_login)
3022-
3023- @dbus.service.method(dbus_interface=DBUS_IFACE_AUTH_NAME,
3024- in_signature='ss', out_signature='')
3025- def clear_token(self, realm, consumer_key):
3026- """D-Bus method, to clear the existing token."""
3027- self.processor.clear_token(realm, consumer_key)
3028-
3029- @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME, signature='ss')
3030- def NewCredentials(self, realm, consumer_key):
3031- """Fire D-Bus signal when the user accepts authorization."""
3032- logger.debug("Firing the NewCredentials signal")
3033- self.currently_authing = False
3034- return (self.processor.realm, self.processor.consumer_key)
3035-
3036- @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME)
3037- def AuthorizationDenied(self):
3038- """Fire the signal when the user denies authorization."""
3039- self.currently_authing = False
3040-
3041- @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME)
3042- def NoCredentials(self):
3043- """Fired when the user does not have a token in the keyring."""
3044- self.currently_authing = False
3045-
3046- @dbus.service.signal(dbus_interface=DBUS_IFACE_AUTH_NAME, signature='s')
3047- def OAuthError(self, message):
3048- """Fire the signal when an error needs to be propagated to the user."""
3049- self.currently_authing = False
3050- return message
3051-
3052-
3053-def main():
3054- """Start everything"""
3055- dbus.mainloop.glib.threads_init()
3056- gobject.threads_init()
3057- logger.debug("Starting up at %s", time.asctime())
3058- logger.debug("Installing the Twisted glib2reactor")
3059- from twisted.internet import glib2reactor # for non-GUI apps
3060- glib2reactor.install()
3061- from twisted.internet import reactor
3062-
3063- logger.debug("Creating the D-Bus service")
3064- Login(dbus.service.BusName(DBUS_BUS_NAME,
3065- bus=dbus.SessionBus()))
3066- SSOLogin(dbus.service.BusName(DBUS_BUS_NAME,
3067- bus=dbus.SessionBus()))
3068- SSOCredentials(dbus.service.BusName(DBUS_BUS_NAME,
3069- bus=dbus.SessionBus()),
3070- object_path=DBUS_CRED_PATH)
3071- # cleverness here to say:
3072- # am I already running (bound to this d-bus name)?
3073- # if so, send a signal to the already running instance
3074- # this means that this app can be started from an x-ubutnuone: URL
3075- # to kick off the signin process
3076- logger.debug("Starting the reactor mainloop")
3077- reactor.run()
3078
3079=== modified file 'ubuntu_sso/networkstate.py'
3080--- ubuntu_sso/networkstate.py 2010-08-11 17:03:11 +0000
3081+++ ubuntu_sso/networkstate.py 2010-09-09 14:47:44 +0000
3082@@ -19,13 +19,13 @@
3083
3084 import dbus
3085
3086-from ubuntu_sso.logger import setupLogging
3087-logger = setupLogging("ubuntu_sso.networkstate")
3088+from ubuntu_sso.logger import setup_logging
3089+logger = setup_logging("ubuntu_sso.networkstate")
3090
3091 # Values returned by the callback
3092 ONLINE, OFFLINE, UNKNOWN = object(), object(), object()
3093
3094-nm_state_names = {
3095+NM_STATE_NAMES = {
3096 ONLINE: "online",
3097 OFFLINE: "offline",
3098 UNKNOWN: "unknown",
3099@@ -46,10 +46,10 @@
3100 class NetworkManagerState(object):
3101 """Checks the state of NetworkManager thru DBus."""
3102
3103- def __init__(self, result_cb, dbus=dbus):
3104+ def __init__(self, result_cb, dbus_module=dbus):
3105 """Initialize this instance with a result and error callbacks."""
3106 self.result_cb = result_cb
3107- self.dbus = dbus
3108+ self.dbus = dbus_module
3109 self.state_signal = None
3110
3111 def call_result_cb(self, state):
3112@@ -102,5 +102,5 @@
3113 nm_proxy.Get(NM_DBUS_INTERFACE, "State",
3114 reply_handler=self.got_state,
3115 error_handler=self.got_error)
3116- except Exception, e:
3117+ except Exception, e: # pylint: disable=W0703
3118 self.got_error(e)
3119
3120=== added directory 'ubuntu_sso/tests/bin'
3121=== added file 'ubuntu_sso/tests/bin/show_gui'
3122--- ubuntu_sso/tests/bin/show_gui 1970-01-01 00:00:00 +0000
3123+++ ubuntu_sso/tests/bin/show_gui 2010-09-09 14:47:44 +0000
3124@@ -0,0 +1,57 @@
3125+#!/usr/bin/python
3126+
3127+# ubuntu-sso-login-gui - GUI for registration and login for Ubuntu SSO
3128+#
3129+# Author: Natalia Bidart <natalia.bidart@canonical.com>
3130+#
3131+# Copyright 2010 Canonical Ltd.
3132+#
3133+# This program is free software: you can redistribute it and/or modify it
3134+# under the terms of the GNU General Public License version 3, as published
3135+# by the Free Software Foundation.
3136+#
3137+# This program is distributed in the hope that it will be useful, but
3138+# WITHOUT ANY WARRANTY; without even the implied warranties of
3139+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3140+# PURPOSE. See the GNU General Public License for more details.
3141+#
3142+# You should have received a copy of the GNU General Public License along
3143+# with this program. If not, see <http://www.gnu.org/licenses/>.
3144+"""Script to run the Ubuntu SSO client GUI."""
3145+
3146+import gtk
3147+import sys
3148+
3149+from ubuntu_sso.gui import UbuntuSSOClientGUI
3150+
3151+
3152+APP_NAME = 'Ubuntu'
3153+TC_URI = 'http://one.ubuntu.com/terms'
3154+HELP_TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' \
3155+ 'Nam sed lorem nibh. Suspendisse gravida nulla non nunc suscipit' \
3156+ ' pulvinar tempus ut augue.'
3157+
3158+main_quit = lambda *args, **kwargs: gtk.main_quit()
3159+
3160+
3161+if __name__ == '__main__':
3162+ tc_uri=TC_URI
3163+ login_only = False
3164+ xid = 0
3165+
3166+ if '--login' in sys.argv:
3167+ login_only = True
3168+ tc_uri = None
3169+
3170+ if '--with-parent-window' in sys.argv:
3171+ win = gtk.Window()
3172+ win.set_title(APP_NAME)
3173+ win.set_position(gtk.WIN_POS_CENTER)
3174+ win.set_size_request(800, 600)
3175+ win.show()
3176+ xid = win.window.xid
3177+
3178+ UbuntuSSOClientGUI(close_callback=main_quit, app_name=APP_NAME,
3179+ tc_uri=tc_uri, help_text=HELP_TEXT, window_id=xid,
3180+ login_only=login_only)
3181+ gtk.main()
3182
3183=== added file 'ubuntu_sso/tests/bin/show_nm_state'
3184--- ubuntu_sso/tests/bin/show_nm_state 1970-01-01 00:00:00 +0000
3185+++ ubuntu_sso/tests/bin/show_nm_state 2010-09-09 14:47:44 +0000
3186@@ -0,0 +1,41 @@
3187+#!/usr/bin/python
3188+
3189+# show_nm_state - Show the state of the NetworkManager daemon
3190+#
3191+# Author: Alejandro J. Cura <alecu@canonical.com>
3192+#
3193+# Copyright 2010 Canonical Ltd.
3194+#
3195+# This program is free software: you can redistribute it and/or modify it
3196+# under the terms of the GNU General Public License version 3, as published
3197+# by the Free Software Foundation.
3198+#
3199+# This program is distributed in the hope that it will be useful, but
3200+# WITHOUT ANY WARRANTY; without even the implied warranties of
3201+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3202+# PURPOSE. See the GNU General Public License for more details.
3203+#
3204+# You should have received a copy of the GNU General Public License along
3205+# with this program. If not, see <http://www.gnu.org/licenses/>.
3206+"""A test program that just shows the network state."""
3207+
3208+import gobject
3209+
3210+from dbus.mainloop.glib import DBusGMainLoop
3211+
3212+from ubuntu_sso.networkstate import NetworkManagerState, NM_STATE_NAMES
3213+
3214+DBusGMainLoop(set_as_default=True)
3215+loop = gobject.MainLoop()
3216+
3217+
3218+def got_state(state):
3219+ """Called when the network state is found."""
3220+ try:
3221+ print NM_STATE_NAMES[state]
3222+ finally:
3223+ loop.quit()
3224+
3225+nms = NetworkManagerState(got_state)
3226+nms.find_online_state()
3227+loop.run()
3228
3229=== added directory 'ubuntu_sso/tests/files'
3230=== added file 'ubuntu_sso/tests/files/captcha.png'
3231Binary files ubuntu_sso/tests/files/captcha.png 1970-01-01 00:00:00 +0000 and ubuntu_sso/tests/files/captcha.png 2010-09-09 14:47:44 +0000 differ
3232=== removed file 'ubuntu_sso/tests/test_auth.py'
3233--- ubuntu_sso/tests/test_auth.py 2010-06-16 15:11:04 +0000
3234+++ ubuntu_sso/tests/test_auth.py 1970-01-01 00:00:00 +0000
3235@@ -1,261 +0,0 @@
3236-# test_auth - Tests for ubuntu_sso.auth module
3237-#
3238-# Author: Stuart Langridge <stuart.langridge@canonical.com>
3239-#
3240-# Copyright 2009 Canonical Ltd.
3241-#
3242-# This program is free software: you can redistribute it and/or modify it
3243-# under the terms of the GNU General Public License version 3, as published
3244-# by the Free Software Foundation.
3245-#
3246-# This program is distributed in the hope that it will be useful, but
3247-# WITHOUT ANY WARRANTY; without even the implied warranties of
3248-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3249-# PURPOSE. See the GNU General Public License for more details.
3250-#
3251-# You should have received a copy of the GNU General Public License along
3252-# with this program. If not, see <http://www.gnu.org/licenses/>.
3253-"""Tests for the OAuth client code for StorageFS."""
3254-
3255-import gnomekeyring
3256-from mocker import ANY, IN, MockerTestCase
3257-import testresources
3258-
3259-from oauth import oauth
3260-from twisted.trial.unittest import TestCase as TwistedTestCase
3261-from ubuntu_sso.auth import AuthorisationClient, NoAccessToken
3262-
3263-
3264-# Adding TwistedTestCase as parent to be able to skip
3265-# test_ensure_access_token_no_token
3266-class AuthorisationClientTests(MockerTestCase, TwistedTestCase):
3267- """Test the GNOME keyring integration portions of the auth code."""
3268-
3269- def setUp(self):
3270- """Sets up a mock keyring."""
3271- MockerTestCase.setUp(self)
3272- self.keyring = self.mocker.mock()
3273- self.client = None
3274- self.item = self.mocker.mock(gnomekeyring.Found)
3275-
3276- self.item_id = 999
3277-
3278- ex = self.expect(self.item.item_id)
3279- ex.result(self.item_id)
3280- ex.count(0, None)
3281-
3282- ex = self.expect(self.item.secret)
3283- ex.result('oauth_token=access_key&oauth_token_secret=access_secret')
3284- ex.count(0, None)
3285-
3286- def expect_token_query(self):
3287- """Expects the keyring to be queried for a token."""
3288- return self.expect(
3289- self.keyring.find_items_sync(
3290- gnomekeyring.ITEM_GENERIC_SECRET,
3291- {'ubuntuone-realm': 'realm',
3292- 'oauth-consumer-key': 'consumer_key'})
3293- )
3294-
3295- def expect_token_store(self):
3296- """Expects the token to be stored in the keyring."""
3297- return self.expect(self.keyring.item_create_sync(
3298- None, gnomekeyring.ITEM_GENERIC_SECRET,
3299- 'UbuntuOne token for realm',
3300- {'ubuntuone-realm': 'realm',
3301- 'oauth-consumer-key': 'consumer_key'},
3302- # Either order for the token and secret is valid
3303- IN(['oauth_token=access_key&oauth_token_secret=access_secret',
3304- 'oauth_token_secret=access_secret&oauth_token=access_key']),
3305- True))
3306-
3307- def expect_token_store_denied(self):
3308- """Expects the token to be denied for storing in keyring."""
3309- self.expect_token_store().throw(gnomekeyring.DeniedError)
3310-
3311- def mock_has_token(self):
3312- """Mocks a cached token in the keyring."""
3313- self.expect_token_query().result([self.item])
3314-
3315- def mock_no_token(self, exception):
3316- """Mocks no token in the keyring."""
3317- self.expect_token_query().throw(exception)
3318-
3319- def replay(self, callback_parent=None, callback_denied=None,
3320- do_login=True):
3321- """Starts the replay phase and sets up a client object to be tested,
3322- wired up to the mock keyring.
3323-
3324- """
3325- self.mocker.replay()
3326- self.client = AuthorisationClient(
3327- 'realm', 'request_token_url', 'user_authorisation_url',
3328- 'access_token_url', 'consumer_key', 'consumer_secret',
3329- callback_parent, callback_denied, do_login, keyring=self.keyring)
3330-
3331- def test_get_access_token(self):
3332- """The get_access_token method returns the access token"""
3333- self.mock_has_token()
3334- self.replay()
3335- token = self.client.get_access_token()
3336- self.assertTrue(isinstance(token, oauth.OAuthToken))
3337- self.assertEqual(token.key, 'access_key')
3338- self.assertEqual(token.secret, 'access_secret')
3339-
3340- def test_get_access_token_no_match(self):
3341- """The get_access_token method fails if there are no matching items"""
3342- self.mock_no_token(gnomekeyring.NoMatchError)
3343- self.replay()
3344- self.assertRaises(NoAccessToken, self.client.get_access_token)
3345-
3346- def test_get_access_token_denied(self):
3347- """The get_access_token method fails if access is denied"""
3348- self.mock_no_token(gnomekeyring.DeniedError)
3349- self.replay()
3350- self.assertRaises(NoAccessToken, self.client.get_access_token)
3351-
3352- def test_have_access_token(self):
3353- """The `have_access_token` method returns True if a the
3354- keyring contains a token."""
3355- self.mock_has_token()
3356- self.replay()
3357- self.assertEqual(self.client.have_access_token(), True)
3358-
3359- def test_have_access_token_fail(self):
3360- """The `have_access_token` method returns False if the keyring
3361- does not contain a token."""
3362- self.mock_no_token(gnomekeyring.NoMatchError)
3363- self.replay()
3364- self.assertEqual(self.client.have_access_token(), False)
3365-
3366- def test_store_token(self):
3367- """The store_token method correctly stores an item in the keyring,
3368- and correctly sets an ACL on it."""
3369- self.expect_token_store().result(self.item_id)
3370- saka = self.mocker.replace("ubuntu_sso.key_acls.set_all_key_acls")
3371- saka(item_id=self.item_id)
3372- self.mocker.result(None)
3373-
3374- sleep = self.mocker.replace("time.sleep")
3375- sleep(4)
3376- self.mocker.result(None)
3377-
3378- self.replay()
3379- self.client.store_token(oauth.OAuthToken('access_key', 'access_secret'))
3380-
3381- def test_store_token_denied(self):
3382- """The store_token method correctly stores an item in the keyring,
3383- and correctly sets an ACL on it."""
3384- self.expect_token_store_denied()
3385-
3386- self.replay()
3387- self.client.store_token(oauth.OAuthToken('access_key', 'access_secret'))
3388-
3389- def test_clear_existing_token(self):
3390- """Makes sure that clear token clears an existing token."""
3391- self.mock_has_token()
3392- self.expect(self.keyring.item_delete_sync(None, self.item_id))
3393- self.replay()
3394- self.client.clear_token()
3395-
3396- def test_clear_no_existing_token(self):
3397- """Makes sure that clear with no existing token still works."""
3398- self.mock_no_token(gnomekeyring.NoMatchError)
3399- self.replay()
3400- self.client.clear_token()
3401-
3402- def test_ensure_access_token(self):
3403- """If the user already has a token, no new token is requested."""
3404- self.mock_has_token()
3405- callback_function = self.mocker.mock()
3406- callback_function(ANY)
3407- self.replay(callback_parent=callback_function)
3408- self.client.ensure_access_token()
3409-
3410- def test_ensure_access_token_no_token(self):
3411- """If the user has no token, a new one is requested and stored, via
3412- an OAuth callback to the internal webserver."""
3413-
3414- self.mock_no_token(gnomekeyring.NoMatchError)
3415-
3416- request_token = self.mocker.mock()
3417- self.expect(request_token.key).result('access_key').count(0, None)
3418- ex = self.expect(request_token.secret)
3419- ex.result('access_secret').count(0, None)
3420- make_token_request = self.mocker.mock()
3421- self.expect(make_token_request(ANY)).result(request_token)
3422-
3423- open_in_browser = self.mocker.mock()
3424- open_in_browser(ANY)
3425-
3426- ri = self.mocker.replace("random.randint")
3427- ri(1000000, 10000000)
3428- self.mocker.result(12345678)
3429- get_temporary_httpd = self.mocker.mock()
3430- get_temporary_httpd(12345678, ANY, True)
3431- self.mocker.result("http://callbackurl")
3432-
3433- self.replay(callback_parent=lambda a: None)
3434-
3435- self.client.make_token_request = make_token_request
3436- self.client.open_in_browser = open_in_browser
3437- self.client.get_temporary_httpd = get_temporary_httpd
3438- # skip the "are we online, via networkmanager" bit
3439- self.client.acquire_access_token_if_online = \
3440- self.client.acquire_access_token
3441- self.client.ensure_access_token()
3442- test_ensure_access_token_no_token.skip = \
3443- "Fails with exceptions.AssertionError: [Mocker] Unmet expectations, "\
3444- "see bug #488933"
3445-
3446-
3447-class AcquireAccessTokenTests(testresources.ResourcedTestCase):
3448- """OAuth token acquisition tests."""
3449-
3450- def setUp(self):
3451- super(AcquireAccessTokenTests, self).setUp()
3452-
3453- def tearDown(self):
3454- super(AcquireAccessTokenTests, self).tearDown()
3455-
3456- def test_acquire_access_token(self):
3457- """Test that acquire_access_token() can acquire the access token."""
3458-
3459- def make_token_request(oauth_request):
3460- """Make an OAuth token request via the test browser."""
3461- return oauth.OAuthToken.from_string("oauth_token=access_token&" +
3462- "oauth_token_secret=" +
3463- "access_secret")
3464-
3465- def open_in_browser(url):
3466- """Just return as we aren't subscribed to the page."""
3467- return
3468-
3469- def got_token(token):
3470- """Called with the token once auth is completed"""
3471- self.assertTrue(isinstance(token, oauth.OAuthToken))
3472-
3473- def get_temporary_httpd(nonce, retrieve_function, store):
3474- """Mock the temporary httpd and return a callback URL"""
3475- # returns callback URL; this is an invalid URL, of course,
3476- # (port is too high) but we check later that the mechanize
3477- # browser tries to navigate there
3478- return "http://localhost:99999/?nonce=99999"
3479-
3480- def store_token(token):
3481- """Don't use the keyring; do nothing"""
3482- pass
3483-
3484- client = AuthorisationClient(
3485- 'http://ubuntuone/',
3486- 'http://ubuntuone/oauth/request/',
3487- 'http://ubuntuone/oauth/authorize/',
3488- 'http://ubuntuone/oauth/access/',
3489- 'consumer_key', 'consumer_secret',
3490- callback_parent=got_token)
3491- client.make_token_request = make_token_request
3492- client.open_in_browser = open_in_browser
3493- client.get_temporary_httpd = get_temporary_httpd
3494- client.store_token = store_token
3495-
3496- client.acquire_access_token('token description')
3497
3498=== removed file 'ubuntu_sso/tests/test_config.py'
3499--- ubuntu_sso/tests/test_config.py 2010-06-16 15:11:04 +0000
3500+++ ubuntu_sso/tests/test_config.py 1970-01-01 00:00:00 +0000
3501@@ -1,85 +0,0 @@
3502-# test_config - tests for ubuntu_sso.config
3503-#
3504-# Author: Stuart Langridge <stuart.langridge@canonical.com>
3505-#
3506-# Copyright 2009 Canonical Ltd.
3507-#
3508-# This program is free software: you can redistribute it and/or modify it
3509-# under the terms of the GNU General Public License version 3, as published
3510-# by the Free Software Foundation.
3511-#
3512-# This program is distributed in the hope that it will be useful, but
3513-# WITHOUT ANY WARRANTY; without even the implied warranties of
3514-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3515-# PURPOSE. See the GNU General Public License for more details.
3516-#
3517-# You should have received a copy of the GNU General Public License along
3518-# with this program. If not, see <http://www.gnu.org/licenses/>.
3519-"""Tests for the OAuth client code for StorageFS."""
3520-
3521-import os, StringIO
3522-from mocker import MockerTestCase
3523-from ubuntu_sso import config
3524-from ubuntu_sso.config import get_config
3525-
3526-class ConfigFileTests(MockerTestCase):
3527- """Test the config file finder."""
3528-
3529- def test_get_file_from_tmp(self):
3530- """Does the tmp file get chosen?"""
3531- # mock that tmp config file exists
3532- tmp_config_file = os.path.realpath(os.path.join(
3533- os.path.split(config.__file__)[0], "../../data/oauth_urls"
3534- ))
3535- osp = self.mocker.replace("os.path")
3536- osp.isfile(tmp_config_file)
3537- self.mocker.result(True)
3538- self.mocker.replay()
3539-
3540- conf = get_config()
3541- self.assertEqual(conf.FILENAME, tmp_config_file)
3542-
3543- def test_get_file_from_home(self):
3544- """Does our home config file get chosen?"""
3545- # mock that home config file exists
3546- home_config_file = os.path.expanduser("~/.config/ubuntu-sso/oauth_urls")
3547- osp = self.mocker.replace("os.path")
3548- osp.exists(home_config_file)
3549- self.mocker.result(True)
3550- self.mocker.replay()
3551-
3552- conf = get_config(use_tmpconfig=False) # pretend not in source tree
3553- self.assertEqual(conf.FILENAME, home_config_file)
3554-
3555- def test_get_file_from_etc(self):
3556- """Does our /etc config file get chosen?"""
3557- # mock that etc config file exists
3558- etc_config_file = os.path.expanduser("/etc/xdg/ubuntu-sso/oauth_urls")
3559- osp = self.mocker.replace("os.path")
3560- osp.exists(etc_config_file)
3561- self.mocker.result(True)
3562- self.mocker.replay()
3563-
3564- conf = get_config(use_tmpconfig=False) # pretend not in source tree
3565- self.assertEqual(conf.FILENAME, etc_config_file)
3566-
3567- def test_tmp_file_parses(self):
3568- """Does the tmp file get chosen and parse correctly?"""
3569- # mock that tmp config file exists
3570- tmp_config_file = os.path.realpath(os.path.join(
3571- os.path.split(config.__file__)[0], "../../data/oauth_urls"
3572- ))
3573- osp = self.mocker.replace("os.path")
3574- osp.isfile(tmp_config_file)
3575- self.mocker.result(True)
3576-
3577- sio = StringIO.StringIO("[default]\n")
3578- mock_open = self.mocker.replace(open)
3579- mock_open(tmp_config_file)
3580- self.mocker.result(sio)
3581-
3582- self.mocker.replay()
3583-
3584- conf = get_config()
3585- self.assertTrue(conf.has_section("default"))
3586-
3587
3588=== modified file 'ubuntu_sso/tests/test_gui.py'
3589--- ubuntu_sso/tests/test_gui.py 2010-08-27 22:16:40 +0000
3590+++ ubuntu_sso/tests/test_gui.py 2010-09-09 14:47:44 +0000
3591@@ -33,6 +33,12 @@
3592 from ubuntu_sso import gui
3593
3594
3595+# Access to a protected member 'yyy' of a client class
3596+# pylint: disable=W0212
3597+# Instance of 'UbuntuSSOClientGUI' has no 'yyy' member
3598+# pylint: disable=E1101,E1103
3599+
3600+
3601 APP_NAME = 'The Super testing app!'
3602 TC_URI = 'http://localhost'
3603 HELP_TEXT = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed
3604@@ -153,6 +159,8 @@
3605 direction = 'in' if focus_in else 'out'
3606 self.entry.emit('focus-%s-event' % direction, None)
3607
3608+ # Bad first argument 'LabeledEntry' given to super class
3609+ # pylint: disable=E1003
3610 def assert_correct_label(self):
3611 """Check that the entry has the correct label."""
3612 # text content is correct
3613@@ -343,7 +351,7 @@
3614 'tc_browser', 'login', 'request_password_token',
3615 'set_new_password')
3616 self.ui = self.gui_class(**self.kwargs)
3617- self.ERROR = {'message': self.ui.UNKNOWN_ERROR}
3618+ self.error = {'message': self.ui.UNKNOWN_ERROR}
3619
3620 def tearDown(self):
3621 """Clean up."""
3622@@ -505,7 +513,7 @@
3623
3624 def test_dbus_signals_are_removed(self):
3625 """The hooked signals are removed at shutdown time."""
3626- self.ui.bus.add_signal_receiver(gui.NO_OP, 'com.iface', 'signal_test')
3627+ self.ui._setup_signals()
3628 assert len(self.ui.bus.callbacks) > 0 # at least one callback
3629
3630 self.ui.on_close_clicked()
3631@@ -552,6 +560,7 @@
3632 self.assertEqual(expected, actual, msg % (label, expected, actual))
3633
3634 def test_password_fields_are_password(self):
3635+ """Password fields have the is_password flag set."""
3636 msg = '"%s" should be a password LabeledEntry instance.'
3637 passwords = filter(lambda name: 'password' in name,
3638 self.ui.entries)
3639@@ -591,7 +600,7 @@
3640 """Main window has the proper icon."""
3641 self.assertEqual('ubuntu-logo', self.ui.window.get_icon_name())
3642
3643- def test_transient_window_is_None_if_window_id_is_zero(self):
3644+ def test_transient_window_is_none_if_window_id_is_zero(self):
3645 """The transient window is correct."""
3646 self.patch(gtk.gdk, 'window_foreign_new', self._set_called)
3647 self.gui_class(window_id=0, **self.kwargs)
3648@@ -837,7 +846,7 @@
3649
3650 def test_join_ok_button_does_nothing_if_clicked_but_disabled(self):
3651 """The join form can only be submitted if the button is sensitive."""
3652- self.patch(self.ui.name_entry, 'get_text', self._set_called)
3653+ self.patch(self.ui.email1_entry, 'get_text', self._set_called)
3654
3655 self.ui.join_ok_button.set_sensitive(False)
3656 self.ui.join_ok_button.clicked()
3657@@ -876,6 +885,10 @@
3658 self.assertIsInstance(browser, webkit.WebView)
3659 self.assertTrue(browser.get_property('visible'))
3660
3661+ settings = browser.get_settings()
3662+ self.assertFalse(settings.get_property('enable-plugins'))
3663+ self.assertFalse(settings.get_property('enable-default-context-menu'))
3664+
3665 self.ui.tc_browser_vbox.hide()
3666
3667 def test_tc_browser_is_destroyed_when_tc_page_is_hid(self):
3668@@ -913,6 +926,9 @@
3669 """Terms & Conditions browser shows the proper uri."""
3670 self.ui.tc_button.clicked()
3671 self.assertEqual(self.ui.tc_browser.get_property('uri'), TC_URI)
3672+
3673+ # Unused variable 'skip'
3674+ # pylint: disable=W0612
3675 test_tc_browser_opens_the_proper_uri.skip = 'The freaking test wont work.'
3676
3677
3678@@ -926,12 +942,12 @@
3679
3680 def test_previous_page_is_shown(self):
3681 """On UserRegistrationError the previous page is shown."""
3682- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
3683+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
3684 self.assert_pages_visibility(enter_details=True)
3685
3686 def test_warning_label_is_shown(self):
3687 """On UserRegistrationError the warning label is shown."""
3688- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
3689+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
3690 self.assert_correct_label_warning(self.ui.warning_label,
3691 self.ui.UNKNOWN_ERROR)
3692
3693@@ -1013,7 +1029,7 @@
3694
3695 def test_on_email_validation_error_verify_email_is_shown(self):
3696 """On email validation error, the verify_email page is shown."""
3697- self.ui.on_email_validation_error(app_name=APP_NAME, error=self.ERROR)
3698+ self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
3699 self.assert_pages_visibility(verify_email=True)
3700 self.assert_correct_label_warning(self.ui.warning_label,
3701 self.ui.UNKNOWN_ERROR)
3702@@ -1116,6 +1132,11 @@
3703 self.ui.FIELD_REQUIRED)
3704 self.assertNotIn('register_user', self.ui.backend._called)
3705
3706+ # Unused variable 'skip'
3707+ # pylint: disable=W0612
3708+ test_warning_is_shown_if_name_empty.skip = \
3709+ 'Unused for now, will be hidden to save space (LP: #627440).'
3710+
3711 def test_warning_is_shown_if_empty_email(self):
3712 """A warning message is shown if emails are empty."""
3713 self.ui.email1_entry.set_text('')
3714@@ -1179,9 +1200,9 @@
3715 def test_warning_is_shown_if_password_too_weak(self):
3716 """A warning message is shown if password is too weak."""
3717 # password will match but will be too weak
3718- for w in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
3719- self.ui.password1_entry.set_text(w)
3720- self.ui.password2_entry.set_text(w)
3721+ for pwd in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
3722+ self.ui.password1_entry.set_text(pwd)
3723+ self.ui.password2_entry.set_text(pwd)
3724
3725 self.ui.join_ok_button.clicked()
3726
3727@@ -1304,13 +1325,13 @@
3728 def test_on_login_error_morphs_to_login_page(self):
3729 """On user login error, the previous page is shown."""
3730 self.click_connect_with_valid_data()
3731- self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
3732+ self.ui.on_login_error(app_name=APP_NAME, error=self.error)
3733 self.assert_pages_visibility(login=True)
3734
3735 def test_on_login_error_a_warning_is_shown(self):
3736 """On user login error, a warning is shown with proper wording."""
3737 self.click_connect_with_valid_data()
3738- self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
3739+ self.ui.on_login_error(app_name=APP_NAME, error=self.error)
3740 self.assert_correct_label_warning(self.ui.warning_label,
3741 self.ui.UNKNOWN_ERROR)
3742
3743@@ -1328,7 +1349,7 @@
3744 def test_back_to_registration_hides_warning(self):
3745 """After user login error, warning is hidden when clicking 'Back'."""
3746 self.click_connect_with_valid_data()
3747- self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
3748+ self.ui.on_login_error(app_name=APP_NAME, error=self.error)
3749 self.ui.login_back_button.clicked()
3750 self.assertFalse(self.ui.warning_label.get_property('visible'))
3751
3752@@ -1504,7 +1525,7 @@
3753
3754 def test_on_password_reset_error_shows_login_page(self):
3755 """When reset token wasn't successfuly sent the login page is shown."""
3756- self.ui.on_password_reset_error(app_name=APP_NAME, error=self.ERROR)
3757+ self.ui.on_password_reset_error(app_name=APP_NAME, error=self.error)
3758 self.assert_correct_label_warning(self.ui.warning_label,
3759 self.ui.UNKNOWN_ERROR)
3760 self.assert_pages_visibility(login=True)
3761@@ -1603,14 +1624,14 @@
3762 entries = (self.ui.reset_code_entry,
3763 self.ui.reset_password1_entry,
3764 self.ui.reset_password2_entry)
3765- for xs in itertools.product(('', ' ', 'a'), repeat=3):
3766+ for values in itertools.product(('', ' ', 'a'), repeat=3):
3767 expected = True
3768- for entry, x in zip(entries, xs):
3769- entry.set_text(x)
3770- expected &= bool(x and not x.isspace())
3771+ for entry, val in zip(entries, values):
3772+ entry.set_text(val)
3773+ expected &= bool(val and not val.isspace())
3774
3775 actual = self.ui.set_new_password_ok_button.get_sensitive()
3776- self.assertEqual(expected, actual, msg % (expected, xs))
3777+ self.assertEqual(expected, actual, msg % (expected, values))
3778
3779 def test_on_set_new_password_ok_button_clicked_morphs_window(self):
3780 """Clicking set_new_password_ok_button the processing page is shown."""
3781@@ -1636,7 +1657,7 @@
3782
3783 def test_on_password_change_error_shows_login_page(self):
3784 """When password wasn't changed the reset password page is shown."""
3785- self.ui.on_password_change_error(app_name=APP_NAME, error=self.ERROR)
3786+ self.ui.on_password_change_error(app_name=APP_NAME, error=self.error)
3787 self.assert_correct_label_warning(self.ui.warning_label,
3788 self.ui.UNKNOWN_ERROR)
3789 self.assert_pages_visibility(request_password_token=True)
3790@@ -1723,9 +1744,9 @@
3791 def test_warning_is_shown_if_password_too_weak(self):
3792 """A warning message is shown if password is too weak."""
3793 # password will match but will be too weak
3794- for w in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
3795- self.ui.reset_password1_entry.set_text(w)
3796- self.ui.reset_password2_entry.set_text(w)
3797+ for pwd in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
3798+ self.ui.reset_password1_entry.set_text(pwd)
3799+ self.ui.reset_password2_entry.set_text(pwd)
3800
3801 self.ui.set_new_password_ok_button.set_sensitive(True)
3802 self.ui.set_new_password_ok_button.clicked()
3803@@ -1759,26 +1780,118 @@
3804
3805 def test_all_the_signals_are_listed(self):
3806 """All the backend signals are listed to be binded."""
3807- names = [sig for (sig, cb) in self.ui._signals]
3808 for sig in ('CaptchaGenerated', 'CaptchaGenerationError',
3809 'UserRegistered', 'UserRegistrationError',
3810 'LoggedIn', 'LoginError',
3811 'EmailValidated', 'EmailValidationError',
3812 'PasswordResetTokenSent', 'PasswordResetError',
3813 'PasswordChanged', 'PasswordChangeError'):
3814- self.assertIn(sig, names)
3815+ self.assertIn(sig, self.ui._signals)
3816
3817 def test_signal_receivers_are_connected(self):
3818 """Callbacks are connected to signals of interest."""
3819 msg1 = 'callback %r for signal %r must be added to the internal bus.'
3820 msg2 = 'callback %r for signal %r must be added to the ui log.'
3821 dbus_iface = self.ui.iface_name
3822- for signal, method in self.ui._signals:
3823+ for signal, method in self.ui._signals.iteritems():
3824 actual = self.ui.bus.callbacks.get((dbus_iface, signal))
3825 self.assertEqual(method, actual, msg1 % (method, signal))
3826 actual = self.ui._signals_receivers.get((dbus_iface, signal))
3827 self.assertEqual(method, actual, msg2 % (method, signal))
3828
3829+ def test_callbacks_only_log_when_app_name_doesnt_match(self):
3830+ """Callbacks do nothing but logging when app_name doesn't match."""
3831+ mismatch_app_name = self.ui.app_name * 2
3832+ for method in self.ui._signals.itervalues():
3833+ msgs = ('ignoring', method.__name__, mismatch_app_name)
3834+ method(mismatch_app_name, 'dummy')
3835+ self.assertTrue(self.memento.check(logging.INFO, *msgs))
3836+ self.memento.records = []
3837+
3838+ def test_on_captcha_generated_is_not_called(self):
3839+ """on_captcha_generated is not called if incorrect app_name."""
3840+ self.patch(self.ui, 'on_captcha_generated', self._set_called)
3841+ mismatch_app_name = self.ui.app_name * 2
3842+ self.ui._signals['CaptchaGenerated'](mismatch_app_name, 'dummy')
3843+ self.assertFalse(self._called)
3844+
3845+ def test_on_captcha_generation_error_is_not_called(self):
3846+ """on_captcha_generation_error is not called if incorrect app_name."""
3847+ self.patch(self.ui, 'on_captcha_generation_error', self._set_called)
3848+ mismatch_app_name = self.ui.app_name * 2
3849+ self.ui._signals['CaptchaGenerationError'](mismatch_app_name, 'dummy')
3850+ self.assertFalse(self._called)
3851+
3852+ def test_on_user_registered_is_not_called(self):
3853+ """on_user_registered is not called if incorrect app_name."""
3854+ self.patch(self.ui, 'on_user_registered', self._set_called)
3855+ mismatch_app_name = self.ui.app_name * 2
3856+ self.ui._signals['UserRegistered'](mismatch_app_name, 'dummy')
3857+ self.assertFalse(self._called)
3858+
3859+ def test_on_user_registration_error_is_not_called(self):
3860+ """on_user_registration_error is not called if incorrect app_name."""
3861+ self.patch(self.ui, 'on_user_registration_error', self._set_called)
3862+ mismatch_app_name = self.ui.app_name * 2
3863+ self.ui._signals['UserRegistrationError'](mismatch_app_name, 'dummy')
3864+ self.assertFalse(self._called)
3865+
3866+ def test_on_email_validated_is_not_called(self):
3867+ """on_email_validated is not called if incorrect app_name."""
3868+ self.patch(self.ui, 'on_email_validated', self._set_called)
3869+ mismatch_app_name = self.ui.app_name * 2
3870+ self.ui._signals['EmailValidated'](mismatch_app_name, 'dummy')
3871+ self.assertFalse(self._called)
3872+
3873+ def test_on_email_validation_error_is_not_called(self):
3874+ """on_email_validation_error is not called if incorrect app_name."""
3875+ self.patch(self.ui, 'on_email_validation_error', self._set_called)
3876+ mismatch_app_name = self.ui.app_name * 2
3877+ self.ui._signals['EmailValidationError'](mismatch_app_name, 'dummy')
3878+ self.assertFalse(self._called)
3879+
3880+ def test_on_logged_in_is_not_called(self):
3881+ """on_logged_in is not called if incorrect app_name."""
3882+ self.patch(self.ui, 'on_logged_in', self._set_called)
3883+ mismatch_app_name = self.ui.app_name * 2
3884+ self.ui._signals['LoggedIn'](mismatch_app_name, 'dummy')
3885+ self.assertFalse(self._called)
3886+
3887+ def test_on_login_error_is_not_called(self):
3888+ """on_login_error is not called if incorrect app_name."""
3889+ self.patch(self.ui, 'on_login_error', self._set_called)
3890+ mismatch_app_name = self.ui.app_name * 2
3891+ self.ui._signals['LoginError'](mismatch_app_name, 'dummy')
3892+ self.assertFalse(self._called)
3893+
3894+ def test_on_password_reset_token_sent_is_not_called(self):
3895+ """on_password_reset_token_sent is not called if incorrect app_name."""
3896+ self.patch(self.ui, 'on_password_reset_token_sent', self._set_called)
3897+ mismatch_app_name = self.ui.app_name * 2
3898+ self.ui._signals['PasswordResetTokenSent'](mismatch_app_name, 'dummy')
3899+ self.assertFalse(self._called)
3900+
3901+ def test_on_password_reset_error_is_not_called(self):
3902+ """on_password_reset_error is not called if incorrect app_name."""
3903+ self.patch(self.ui, 'on_password_reset_error', self._set_called)
3904+ mismatch_app_name = self.ui.app_name * 2
3905+ self.ui._signals['PasswordResetError'](mismatch_app_name, 'dummy')
3906+ self.assertFalse(self._called)
3907+
3908+ def test_on_password_changed_is_not_called(self):
3909+ """on_password_changed is not called if incorrect app_name."""
3910+ self.patch(self.ui, 'on_password_changed', self._set_called)
3911+ mismatch_app_name = self.ui.app_name * 2
3912+ self.ui._signals['PasswordChanged'](mismatch_app_name, 'dummy')
3913+ self.assertFalse(self._called)
3914+
3915+ def test_on_password_change_error_is_not_called(self):
3916+ """on_password_change_error is not called if incorrect app_name."""
3917+ self.patch(self.ui, 'on_password_change_error', self._set_called)
3918+ mismatch_app_name = self.ui.app_name * 2
3919+ self.ui._signals['PasswordChangeError'](mismatch_app_name, 'dummy')
3920+ self.assertFalse(self._called)
3921+
3922
3923 class LoginOnlyTestCase(UbuntuSSOClientTestCase):
3924 """Test suite for the login only GUI."""
3925@@ -1804,6 +1917,7 @@
3926
3927
3928 class SignalsTestCase(UbuntuSSOClientTestCase):
3929+ """Test the GTK signal emission."""
3930
3931 def setUp(self):
3932 """Init."""
3933@@ -1814,8 +1928,15 @@
3934
3935 def _set_called(self, widget, *args, **kwargs):
3936 """Keep trace of signals emition."""
3937+ # pylint: disable=W0221
3938 self._called[args[-1]] = (widget, args[:-1], kwargs)
3939
3940+ def test_closing_main_window_sends_outcome_as_signal(self):
3941+ """A signal is sent when closing the main window."""
3942+ self.ui.window.emit('delete-event', gtk.gdk.Event(gtk.gdk.DELETE))
3943+ expected = (self.ui.window, (APP_NAME,), {})
3944+ self.assertEqual(self._called[gui.SIG_USER_CANCELATION], expected)
3945+
3946 def test_every_cancel_emits_proper_signal(self):
3947 """Clicking on any cancel button, 'user-cancelation' signal is sent."""
3948 sig = gui.SIG_USER_CANCELATION
3949@@ -1832,7 +1953,7 @@
3950
3951 def test_on_user_registration_error_proper_signal_is_emitted(self):
3952 """On UserRegistrationError, 'registration-failed' signal is sent."""
3953- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
3954+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
3955 self.ui.on_close_clicked()
3956 expected = (self.ui.window, (APP_NAME, self.ui.UNKNOWN_ERROR), {})
3957 self.assertEqual(expected,
3958@@ -1847,7 +1968,7 @@
3959
3960 def test_on_email_validation_error_proper_signals_is_emitted(self):
3961 """On EmailValidationError, 'registration-failed' signal is sent."""
3962- self.ui.on_email_validation_error(app_name=APP_NAME, error=self.ERROR)
3963+ self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
3964 self.ui.on_close_clicked()
3965 expected = (self.ui.window, (APP_NAME, self.ui.UNKNOWN_ERROR), {})
3966 self.assertEqual(expected,
3967@@ -1863,7 +1984,7 @@
3968 def test_on_login_error_proper_signals_is_emitted(self):
3969 """On LoginError, 'login-failed' signal is sent."""
3970 self.click_connect_with_valid_data()
3971- self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
3972+ self.ui.on_login_error(app_name=APP_NAME, error=self.error)
3973 self.ui.on_close_clicked()
3974 expected = (self.ui.window, (APP_NAME, self.ui.UNKNOWN_ERROR), {})
3975 self.assertEqual(expected,
3976@@ -1872,7 +1993,7 @@
3977 def test_registration_successfull_even_if_prior_registration_error(self):
3978 """Only one signal is sent with the final outcome."""
3979 self.click_join_with_valid_data()
3980- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
3981+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
3982 self.click_join_with_valid_data()
3983 self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
3984 self.ui.on_close_clicked()
3985@@ -1883,7 +2004,7 @@
3986 def test_login_successfull_even_if_prior_login_error(self):
3987 """Only one signal is sent with the final outcome."""
3988 self.click_connect_with_valid_data()
3989- self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
3990+ self.ui.on_login_error(app_name=APP_NAME, error=self.error)
3991 self.click_connect_with_valid_data()
3992 self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
3993 self.ui.on_close_clicked()
3994@@ -1894,7 +2015,7 @@
3995 def test_user_cancelation_even_if_prior_registration_error(self):
3996 """Only one signal is sent with the final outcome."""
3997 self.click_join_with_valid_data()
3998- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.ERROR)
3999+ self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
4000 self.ui.join_cancel_button.clicked()
4001
4002 self.assertEqual(len(self._called), 1)
4003@@ -1903,8 +2024,56 @@
4004 def test_user_cancelation_even_if_prior_login_error(self):
4005 """Only one signal is sent with the final outcome."""
4006 self.click_connect_with_valid_data()
4007- self.ui.on_login_error(app_name=APP_NAME, error=self.ERROR)
4008+ self.ui.on_login_error(app_name=APP_NAME, error=self.error)
4009 self.ui.login_cancel_button.clicked()
4010
4011 self.assertEqual(len(self._called), 1)
4012 self.assertTrue(gui.SIG_USER_CANCELATION in self._called)
4013+
4014+
4015+class DefaultButtonsTestCase(UbuntuSSOClientTestCase):
4016+ """Each UI page has a default button when visible."""
4017+
4018+ def setUp(self):
4019+ """Init."""
4020+ super(DefaultButtonsTestCase, self).setUp()
4021+ self.mapping = (
4022+ ('enter_details_vbox', 'join_ok_button'),
4023+ ('tc_browser_vbox', 'tc_back_button'),
4024+ ('verify_email_vbox', 'verify_token_button'),
4025+ ('login_vbox', 'login_ok_button'),
4026+ ('request_password_token_vbox',
4027+ 'request_password_token_ok_button'),
4028+ ('set_new_password_vbox', 'set_new_password_ok_button'),
4029+ ('success_vbox', 'success_close_button'),
4030+ ('processing_vbox', None))
4031+
4032+ def test_pages_have_default_widget_set(self):
4033+ """Each page has a proper button as default widget."""
4034+ msg = 'Page %r must have %r as default_widget (got %r instead).'
4035+ for pname, bname in self.mapping:
4036+ page = getattr(self.ui, pname)
4037+ button = bname and getattr(self.ui, bname)
4038+ self.assertTrue(page.default_widget is button,
4039+ msg % (pname, bname, page.default_widget))
4040+
4041+ def test_default_widget_can_default(self):
4042+ """Each default button can default."""
4043+ msg = 'Button %r must have can-default enabled.'
4044+ for _, bname in self.mapping:
4045+ if bname is not None:
4046+ button = getattr(self.ui, bname)
4047+ self.assertTrue(button.get_property('can-default'),
4048+ msg % bname)
4049+
4050+ def test_set_current_page_grabs_focus_for_default_button(self):
4051+ """Setting the current page marks the default widget as default."""
4052+ msg = '%r "has_default" must be True when %r if the current page.'
4053+ for pname, bname in self.mapping:
4054+ if bname is not None:
4055+ page = getattr(self.ui, pname)
4056+ self.patch(page.default_widget, 'grab_default',
4057+ self._set_called)
4058+ self.ui._set_current_page(page)
4059+ self.assertEqual(self._called, ((), {}), msg % (bname, pname))
4060+ self._called = False
4061
4062=== removed file 'ubuntu_sso/tests/test_key_acls.py'
4063--- ubuntu_sso/tests/test_key_acls.py 2010-06-16 15:11:04 +0000
4064+++ ubuntu_sso/tests/test_key_acls.py 1970-01-01 00:00:00 +0000
4065@@ -1,166 +0,0 @@
4066-# test_key_acls - tests for ubuntu_sso.key_acls
4067-#
4068-# Author: Stuart Langridge <stuart.langridge@canonical.com>
4069-#
4070-# Copyright 2009 Canonical Ltd.
4071-#
4072-# This program is free software: you can redistribute it and/or modify it
4073-# under the terms of the GNU General Public License version 3, as published
4074-# by the Free Software Foundation.
4075-#
4076-# This program is distributed in the hope that it will be useful, but
4077-# WITHOUT ANY WARRANTY; without even the implied warranties of
4078-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4079-# PURPOSE. See the GNU General Public License for more details.
4080-#
4081-# You should have received a copy of the GNU General Public License along
4082-# with this program. If not, see <http://www.gnu.org/licenses/>.
4083-"""Tests for the ACL setting code for ubuntuone-oauth-login."""
4084-
4085-import os, StringIO
4086-import xdg.BaseDirectory
4087-from mocker import MockerTestCase, IN, ANY
4088-from ubuntu_sso.key_acls import (
4089- get_privileged_config_folder, get_acl_preregistrations,
4090- set_all_key_acls, set_single_acl
4091-)
4092-
4093-ETC_FOLDER = "/etc/xdg/ubuntu-sso"
4094-ETC_FILES_FOLDER = "/etc/xdg/ubuntu-sso/oauth_registration.d"
4095-
4096-class Loader(MockerTestCase):
4097- """Confirm only the /etc/xdg config files are loaded."""
4098-
4099- def test_etc_found(self):
4100- """Is the /etc/xdg folder found if present?"""
4101- osp = self.mocker.replace("os.path")
4102- osp.exists(ETC_FOLDER)
4103- self.mocker.result(True)
4104- self.mocker.replay()
4105-
4106- self.assertEqual(ETC_FOLDER,
4107- get_privileged_config_folder(use_source_tree_folder=False))
4108-
4109- def test_etc_found_despite_home(self):
4110- """Is the /etc/xdg folder found even if the $HOME folder is present?"""
4111- HOME_FOLDER = os.path.join(xdg.BaseDirectory.xdg_config_home,
4112- "ubuntu-sso")
4113- osp = self.mocker.replace("os.path")
4114- osp.exists(ETC_FOLDER)
4115- self.mocker.result(True)
4116- osp.exists(HOME_FOLDER)
4117- self.mocker.result(True)
4118- self.mocker.replay()
4119-
4120- self.assertEqual(ETC_FOLDER,
4121- get_privileged_config_folder(use_source_tree_folder=False))
4122-
4123- def test_etc_files_found(self):
4124- """Are files in /etc found?"""
4125- FILE_LIST = ["a", "b"]
4126- osp = self.mocker.replace("os.path")
4127- osp.exists(ETC_FOLDER)
4128- self.mocker.result(True)
4129- osp.isdir(ETC_FILES_FOLDER)
4130- self.mocker.result(True)
4131- listdir = self.mocker.replace("os.listdir")
4132- listdir(ETC_FILES_FOLDER)
4133- self.mocker.result(FILE_LIST)
4134- self.mocker.replay()
4135-
4136- our_list = sorted(
4137- [os.path.join(ETC_FILES_FOLDER, x) for x in FILE_LIST])
4138- their_list = sorted(
4139- get_acl_preregistrations(use_source_tree_folder=False))
4140- self.assertEqual(our_list, their_list)
4141-
4142- def test_set_single_acl(self):
4143- """Does set_single_acl work?"""
4144- giifr = self.mocker.replace("ubuntu_sso.key_acls.get_item_ids_for_realm")
4145- igas = self.mocker.replace("gnomekeyring.item_get_acl_sync")
4146- isas = self.mocker.replace("gnomekeyring.item_set_acl_sync")
4147- acon = self.mocker.replace("gnomekeyring.AccessControl")
4148- self.expect(giifr("realm", "consumer_key")).result([999])
4149- self.expect(igas(None, 999)).result([])
4150- ac1 = self.mocker.mock()
4151- self.expect(acon(ANY, ANY)).result(ac1)
4152- ac1.set_display_name("application_name")
4153- ac1.set_path_name("/tmp/exe_path")
4154- self.expect(isas(None, 999, [ac1])).result(None)
4155- self.expect(giifr("realm2", "consumer_key2")).result([9999])
4156- self.expect(igas(None, 9999)).result([])
4157- ac2 = self.mocker.mock()
4158- self.expect(acon(ANY, ANY)).result(ac2)
4159- ac2.set_display_name("application_name2")
4160- ac2.set_path_name("/tmp/exe_path2")
4161- self.expect(isas(None, 9999, [ac2])).result(None)
4162- self.mocker.replay()
4163-
4164- set_single_acl([
4165- ("realm", "consumer_key", "/tmp/exe_path", "application_name"),
4166- ("realm2", "consumer_key2", "/tmp/exe_path2", "application_name2"),
4167- ])
4168-
4169- def test_etc_files_parsed(self):
4170- """Are files in /etc parsed correctly?"""
4171- FILE_LIST = ["a", "b"]
4172- osp = self.mocker.replace("os.path")
4173- osp.exists(ETC_FOLDER)
4174- self.mocker.result(True)
4175- osp.isdir(ETC_FILES_FOLDER)
4176- self.mocker.result(True)
4177- listdir = self.mocker.replace("os.listdir")
4178- listdir(ETC_FILES_FOLDER)
4179- self.mocker.result(FILE_LIST)
4180-
4181- sio1 = StringIO.StringIO("""[app1]
4182-realm = https://realm.example.com/
4183-consumer_key = example_key
4184-exe_path = /nowhere/executable/path
4185-application_name = example_app_name
4186-
4187-[app2]
4188-realm = https://other.example.com/
4189-consumer_key = example_key
4190-exe_path = /nowhere/executable/path
4191-application_name = example_app_name
4192-
4193-""")
4194- sio2 = StringIO.StringIO("""[app3]
4195-exe_path = /nowhere/path/2
4196-application_name = example_app_name2
4197-consumer_key = example_key2
4198-realm = https://realm2.example.com/
4199-""")
4200- mock_open = self.mocker.replace(open)
4201- mock_open(os.path.join(ETC_FILES_FOLDER, "a"))
4202- self.mocker.result(sio1)
4203- mock_open(os.path.join(ETC_FILES_FOLDER, "b"))
4204- self.mocker.result(sio2)
4205-
4206- ssa = self.mocker.replace("ubuntu_sso.key_acls.set_single_acl")
4207- # list may come up in any order
4208- ssa(IN([
4209- [
4210- ("https://realm.example.com/", "example_key",
4211- "/nowhere/executable/path", "example_app_name"),
4212- ("https://other.example.com/", "example_key",
4213- "/nowhere/executable/path", "example_app_name"),
4214- ],
4215- [
4216- ("https://other.example.com/", "example_key",
4217- "/nowhere/executable/path", "example_app_name"),
4218- ("https://realm.example.com/", "example_key",
4219- "/nowhere/executable/path", "example_app_name"),
4220- ],
4221- ]), specific_item_id=None
4222- )
4223- self.mocker.result(None)
4224- ssa([
4225- ("https://realm2.example.com/", "example_key2",
4226- "/nowhere/path/2", "example_app_name2")
4227- ], specific_item_id=None)
4228- self.mocker.result(None)
4229- self.mocker.replay()
4230-
4231- set_all_key_acls(use_source_tree_folder=False)
4232
4233=== modified file 'ubuntu_sso/tests/test_keyring.py'
4234--- ubuntu_sso/tests/test_keyring.py 2010-08-27 22:16:40 +0000
4235+++ ubuntu_sso/tests/test_keyring.py 2010-09-09 14:47:44 +0000
4236@@ -1,6 +1,7 @@
4237 # test_keyring - tests for ubuntu_sso.keyring
4238 #
4239 # Author: Alejandro J. Cura <alecu@canonical.com>
4240+# Author: Natalia B. Bidart <natalia.bidart@canonical.com>
4241 #
4242 # Copyright 2010 Canonical Ltd.
4243 #
4244@@ -26,6 +27,8 @@
4245
4246 from ubuntu_sso import keyring
4247
4248+APP_NAME = 'Yadda Yadda Doo'
4249+
4250
4251 def build_fake_gethostname(fake_hostname):
4252 """Return a fake hostname getter."""
4253@@ -35,19 +38,19 @@
4254 class MockKeyringItem(object):
4255 """A mock object that fakes an item found in a keyring search."""
4256
4257- def __init__(self, item_id, keyring, attributes, secret):
4258+ def __init__(self, item_id, keyring_name, attributes, secret):
4259 """Initialize this instance."""
4260 self.item_id = item_id
4261- self.keyring = keyring
4262+ self.keyring = keyring_name
4263 self.attributes = attributes
4264 self.secret = secret
4265
4266 def matches(self, search_attr):
4267 """See if this item matches a given search."""
4268- for k, v in search_attr.items():
4269+ for k, val in search_attr.items():
4270 if k not in self.attributes:
4271 return False
4272- if self.attributes[k] != v:
4273+ if self.attributes[k] != val:
4274 return False
4275 return True
4276
4277@@ -106,18 +109,29 @@
4278 """A simple token name is built right."""
4279 sample_app_name = "UbuntuTwo"
4280 sample_hostname = "Darkstar"
4281- expected_result = "UbuntuTwo - Darkstar"
4282-
4283- fake_gethostname = build_fake_gethostname(sample_hostname)
4284- self.patch(socket, "gethostname", fake_gethostname)
4285- result = keyring.get_token_name(sample_app_name)
4286- self.assertEqual(result, expected_result)
4287-
4288- def test_get_complex_token_name(self):
4289+ expected_result = "UbuntuTwo @ Darkstar"
4290+
4291+ fake_gethostname = build_fake_gethostname(sample_hostname)
4292+ self.patch(socket, "gethostname", fake_gethostname)
4293+ result = keyring.get_token_name(sample_app_name)
4294+ self.assertEqual(result, expected_result)
4295+
4296+ def test_get_complex_token_name_for_app_name(self):
4297+ """A complex token name is built right too."""
4298+ sample_app_name = "Ubuntu @ Eleven"
4299+ sample_hostname = "Mate+Cocido"
4300+ expected_result = "Ubuntu @ Eleven @ Mate+Cocido"
4301+
4302+ fake_gethostname = build_fake_gethostname(sample_hostname)
4303+ self.patch(socket, "gethostname", fake_gethostname)
4304+ result = keyring.get_token_name(sample_app_name)
4305+ self.assertEqual(result, expected_result)
4306+
4307+ def test_get_complex_token_name_for_hostname(self):
4308 """A complex token name is built right too."""
4309 sample_app_name = "Ubuntu Eleven"
4310- sample_hostname = "Mate+Cocido"
4311- expected_result = "Ubuntu%20Eleven - Mate%2BCocido"
4312+ sample_hostname = "Mate @ Cocido"
4313+ expected_result = "Ubuntu Eleven @ Mate AT Cocido"
4314
4315 fake_gethostname = build_fake_gethostname(sample_hostname)
4316 self.patch(socket, "gethostname", fake_gethostname)
4317@@ -167,6 +181,16 @@
4318 result = keyring.Keyring("appname").get_ubuntusso_attr()
4319 self.assertEqual(result, sample_creds)
4320
4321+ def test_get_credentials_migrating_token(self):
4322+ """Test that credentials are properly retrieved and migrated."""
4323+ sample_creds = {"name": "sample creds name"}
4324+ obj = keyring.Keyring(APP_NAME)
4325+ obj.token_name = keyring.get_old_token_name(APP_NAME)
4326+ obj.set_ubuntusso_attr(sample_creds)
4327+
4328+ result = keyring.Keyring(APP_NAME).get_ubuntusso_attr()
4329+ self.assertEqual(result, sample_creds)
4330+
4331 def test_get_old_cred_found(self):
4332 """The method returns a new set of creds if old creds are found."""
4333 sample_oauth_token = "sample oauth token"
4334
4335=== removed file 'ubuntu_sso/tests/test_login.py'
4336--- ubuntu_sso/tests/test_login.py 2010-08-11 17:03:11 +0000
4337+++ ubuntu_sso/tests/test_login.py 1970-01-01 00:00:00 +0000
4338@@ -1,188 +0,0 @@
4339-# -*- coding: utf-8 -*-
4340-#
4341-# Author: Rodney Dawes <rodney.dawes@canonical.com>
4342-#
4343-# Copyright 2010 Canonical Ltd.
4344-#
4345-# This program is free software: you can redistribute it and/or modify it
4346-# under the terms of the GNU General Public License version 3, as published
4347-# by the Free Software Foundation.
4348-#
4349-# This program is distributed in the hope that it will be useful, but
4350-# WITHOUT ANY WARRANTY; without even the implied warranties of
4351-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4352-# PURPOSE. See the GNU General Public License for more details.
4353-#
4354-# You should have received a copy of the GNU General Public License along
4355-# with this program. If not, see <http://www.gnu.org/licenses/>.
4356-""" Tests for the ubuntuone-login script """
4357-
4358-import dbus.service
4359-import new
4360-import os
4361-
4362-from contrib.testing.testcase import DBusTestCase, FakeLogin
4363-from twisted.internet import defer
4364-from twisted.python.failure import Failure
4365-
4366-
4367-class InvalidSignalError(Exception):
4368- """Exception for when we get the wrong signal called."""
4369- pass
4370-
4371-
4372-class LoginTests(DBusTestCase):
4373- """Basic tests for the ubuntuone-login script """
4374-
4375- _path = os.path.join(os.getcwd(), "bin", "ubuntu-sso-login")
4376- u1login = new.module('u1login')
4377- execfile(_path, u1login.__dict__)
4378-
4379- def setUp(self):
4380- DBusTestCase.setUp(self)
4381- self.oauth = FakeLogin(self.bus)
4382-
4383- def tearDown(self):
4384- # collect all signal receivers registered during the test
4385- signal_receivers = set()
4386- with self.bus._signals_lock:
4387- for group in self.bus._signal_recipients_by_object_path.values():
4388- for matches in group.values():
4389- for match in matches.values():
4390- signal_receivers.update(match)
4391- d = self.cleanup_signal_receivers(signal_receivers)
4392-
4393- def shutdown(r):
4394- """Shutdown."""
4395- self.oauth.shutdown()
4396-
4397- d.addBoth(shutdown)
4398- d.addBoth(lambda _: DBusTestCase.tearDown(self))
4399- return d
4400-
4401- def main(self):
4402- """ Override LoginMain.main """
4403- return
4404-
4405- def test_new_credentials(self):
4406- """ Test logging in """
4407- def new_creds(realm=None, consumer_key=None, sender=None):
4408- """ Override the callback """
4409- d.callback(True)
4410-
4411- def auth_denied():
4412- """ Override the callback """
4413- d.errback(Failure(InvalidSignalError()))
4414-
4415- def got_oauth_error(message=None):
4416- """ Override the callback """
4417- d.errback(Failure(InvalidSignalError()))
4418-
4419- def set_up_desktopcouch_pairing(consumer_key):
4420- """ Override the method """
4421- return
4422-
4423- login = self.u1login.LoginMain()
4424- login.main = self.main
4425- login.new_credentials = new_creds
4426- login.auth_denied = auth_denied
4427- login.got_oauth_error = got_oauth_error
4428- login.set_up_desktopcouch_pairing = set_up_desktopcouch_pairing
4429-
4430- login._connect_dbus_signals()
4431-
4432- client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME,
4433- '/oauthdesktop',
4434- follow_name_owner_changes=True)
4435- d = defer.Deferred()
4436-
4437- def login_handler():
4438- """ login handler """
4439- return
4440-
4441- iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME)
4442- iface.login('http://localhost', self.u1login.OAUTH_CONSUMER,
4443- reply_handler=login_handler,
4444- error_handler=self.error_handler)
4445- return d
4446-
4447- def test_auth_denied(self):
4448- """ Test that denying authorization works correctly. """
4449-
4450- def new_creds(realm=None, consumer_key=None, sender=None):
4451- """ Override the callback """
4452- d.errback(Failure(InvalidSignalError()))
4453-
4454- def auth_denied():
4455- """ Override the callback """
4456- d.callback(True)
4457-
4458- def got_oauth_error(message=None):
4459- """ override the callback """
4460- d.errback(Failure(InvalidSignalError()))
4461-
4462- login = self.u1login.LoginMain()
4463- login.main = self.main
4464- login.new_credentials = new_creds
4465- login.auth_denied = auth_denied
4466- login.got_oauth_error = got_oauth_error
4467-
4468- login._connect_dbus_signals()
4469-
4470- self.oauth.processor.next_login_with(self.oauth.processor.got_denial)
4471-
4472- client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME,
4473- '/oauthdesktop',
4474- follow_name_owner_changes=True)
4475- d = defer.Deferred()
4476-
4477- def login_handler():
4478- """ login handler """
4479- return
4480-
4481- iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME)
4482- iface.login('http://localhost', self.u1login.OAUTH_CONSUMER,
4483- reply_handler=login_handler,
4484- error_handler=self.error_handler)
4485- return d
4486-
4487- def test_oauth_error(self):
4488- """ Test that getting an error works correctly. """
4489-
4490- def new_creds(realm=None, consumer_key=None, sender=None):
4491- """ Override the callback """
4492- d.errback(Failure(InvalidSignalError()))
4493-
4494- def auth_denied():
4495- """ Override the callback """
4496- d.errback(Failure(InvalidSignalError()))
4497-
4498- def got_oauth_error(message=None):
4499- """ override the callback """
4500- d.callback(True)
4501-
4502- login = self.u1login.LoginMain()
4503- login.main = self.main
4504- login.new_credentials = new_creds
4505- login.auth_denied = auth_denied
4506- login.got_oauth_error = got_oauth_error
4507-
4508- login._connect_dbus_signals()
4509-
4510- self.oauth.processor.next_login_with(self.oauth.processor.got_error,
4511- ('error!',))
4512-
4513- client = self.bus.get_object(self.u1login.DBUS_IFACE_AUTH_NAME,
4514- '/oauthdesktop',
4515- follow_name_owner_changes=True)
4516- d = defer.Deferred()
4517-
4518- def login_handler():
4519- """ login handler """
4520- return
4521-
4522- iface = dbus.Interface(client, self.u1login.DBUS_IFACE_AUTH_NAME)
4523- iface.login('http://localhost', self.u1login.OAUTH_CONSUMER,
4524- reply_handler=login_handler,
4525- error_handler=self.error_handler)
4526- return d
4527
4528=== modified file 'ubuntu_sso/tests/test_main.py'
4529--- ubuntu_sso/tests/test_main.py 2010-08-27 22:16:40 +0000
4530+++ ubuntu_sso/tests/test_main.py 2010-09-09 14:47:44 +0000
4531@@ -1,6 +1,5 @@
4532 # test_main - tests for ubuntu_sso.main
4533 #
4534-# Author: Stuart Langridge <stuart.langridge@canonical.com>
4535 # Author: Natalia Bidart <natalia.bidart@canonical.com>
4536 # Author: Alejandro J. Cura <alecu@canonical.com>
4537 #
4538@@ -17,16 +16,18 @@
4539 #
4540 # You should have received a copy of the GNU General Public License along
4541 # with this program. If not, see <http://www.gnu.org/licenses/>.
4542-"""Tests for the OAuth client code."""
4543+"""Tests for the main SSO client code."""
4544
4545 import logging
4546 import os
4547-import StringIO
4548 import urllib2
4549
4550 import gobject
4551
4552+# Unable to import 'lazr.restfulclient.*'
4553+# pylint: disable=F0401
4554 from lazr.restfulclient.errors import HTTPError
4555+# pylint: enable=F0401
4556 from mocker import Mocker, MockerTestCase, ARGS, KWARGS, ANY
4557 from twisted.internet.defer import Deferred
4558 from twisted.trial.unittest import TestCase
4559@@ -35,17 +36,21 @@
4560 import ubuntu_sso.main
4561
4562 from contrib.testing.testcase import MementoHandler
4563-from ubuntu_sso import config, gui
4564+from ubuntu_sso import gui
4565 from ubuntu_sso.keyring import get_token_name, U1_APP_NAME
4566 from ubuntu_sso.main import (
4567- AuthenticationError, BadRealmError, blocking, EmailTokenError,
4568+ AuthenticationError, blocking, EmailTokenError,
4569 except_to_errdict, InvalidEmailError, InvalidPasswordError,
4570 keyring_get_credentials, keyring_store_credentials, logger,
4571- LoginProcessor, NewPasswordError, PING_URL,
4572+ NewPasswordError, PING_URL, SERVICE_URL,
4573 RegistrationError, ResetPasswordTokenError,
4574 SSOCredentials, SSOLogin, SSOLoginProcessor)
4575
4576
4577+# Access to a protected member 'yyy' of a client class
4578+# pylint: disable=W0212
4579+
4580+
4581 APP_NAME = 'The Coolest App Ever'
4582 CAPTCHA_PATH = os.path.abspath(os.path.join(os.curdir, 'ubuntu_sso', 'tests',
4583 'files', 'captcha.png'))
4584@@ -86,8 +91,8 @@
4585 """Fake a urlopen response."""
4586
4587 def __init__(self, *args, **kwargs):
4588- for k, v in kwargs.iteritems():
4589- setattr(self, k, v)
4590+ for k, val in kwargs.iteritems():
4591+ setattr(self, k, val)
4592
4593
4594 class FakedCaptchas(object):
4595@@ -238,9 +243,9 @@
4596 failure = self.assertRaises(RegistrationError,
4597 self.processor.register_user,
4598 **self.register_kwargs)
4599- for k, v in failure.args[0].items():
4600+ for k, val in failure.args[0].items():
4601 self.assertIn(k, STATUS_ERROR['errors'])
4602- self.assertEqual(v, "\n".join(STATUS_ERROR['errors'][k]))
4603+ self.assertEqual(val, "\n".join(STATUS_ERROR['errors'][k]))
4604
4605 def test_register_user_if_status_error_with_string_message(self):
4606 """Proper error is raised if register fails."""
4607@@ -248,9 +253,9 @@
4608 failure = self.assertRaises(RegistrationError,
4609 self.processor.register_user,
4610 **self.register_kwargs)
4611- for k, v in failure.args[0].items():
4612+ for k, val in failure.args[0].items():
4613 self.assertIn(k, {'email': 'Email already registered'})
4614- self.assertEqual(v, 'Email already registered')
4615+ self.assertEqual(val, 'Email already registered')
4616
4617 def test_register_user_if_status_unknown(self):
4618 """Proper error is raised if register returns an unknown status."""
4619@@ -288,9 +293,9 @@
4620 failure = self.assertRaises(EmailTokenError,
4621 self.processor.validate_email,
4622 **self.login_kwargs)
4623- for k, v in failure.args[0].items():
4624+ for k, val in failure.args[0].items():
4625 self.assertIn(k, STATUS_EMAIL_ERROR['errors'])
4626- self.assertEqual(v, "\n".join(STATUS_EMAIL_ERROR['errors'][k]))
4627+ self.assertEqual(val, "\n".join(STATUS_EMAIL_ERROR['errors'][k]))
4628
4629 def test_validate_email_if_status_error_with_string_message(self):
4630 """Proper error is raised if register fails."""
4631@@ -298,9 +303,9 @@
4632 failure = self.assertRaises(EmailTokenError,
4633 self.processor.validate_email,
4634 **self.login_kwargs)
4635- for k, v in failure.args[0].items():
4636+ for k, val in failure.args[0].items():
4637 self.assertIn(k, {'email': 'Email already registered'})
4638- self.assertEqual(v, 'Email already registered')
4639+ self.assertEqual(val, 'Email already registered')
4640
4641 def test_validate_email_if_status_unknown(self):
4642 """Proper error is raised if email validation returns unknown."""
4643@@ -357,72 +362,6 @@
4644 self.assertIn('Received invalid reply: %s' % STATUS_UNKNOWN, exc)
4645
4646
4647-class Realm(MockerTestCase):
4648- """Test the realm handling finder"""
4649-
4650- def test_invalid_realm(self):
4651- """Are invalid realms rejected?"""
4652- login = LoginProcessor(None)
4653- self.assertRaises(BadRealmError, login.login, "bad realm", "key")
4654-
4655- def test_realms(self):
4656- """Are realm URLs correctly retrieved from the config?"""
4657- # mock that tmp config file exists
4658- tmp = os.path.realpath(os.path.join(os.path.split(config.__file__)[0],
4659- "../../data/oauth_urls"))
4660- osp = self.mocker.replace("os.path")
4661- osp.isfile(tmp)
4662- self.mocker.result(True)
4663-
4664- sio = StringIO.StringIO("""[default]
4665-request_token_url = /rtu-default
4666-user_authorisation_url = /uau-default
4667-access_token_url = /atu-default
4668-consumer_secret = foo-default
4669-
4670-[http://localhost]
4671-request_token_url = /rtu-localhost
4672-user_authorisation_url = /uau-localhost
4673-access_token_url = /atu-localhost
4674-consumer_secret = foo-localhost
4675-
4676-[http://ubuntuone.com]
4677-request_token_url = /rtu-ubuntuone
4678-user_authorisation_url = /uau-ubuntuone
4679-access_token_url = /atu-ubuntuone
4680-consumer_secret = foo-ubuntuone
4681-
4682-""")
4683- mock_open = self.mocker.replace(open)
4684- mock_open(tmp)
4685- self.mocker.result(sio)
4686-
4687- self.mocker.replay()
4688-
4689- login = LoginProcessor(None)
4690-
4691- # are localhost:XXXX URLs correctly fetched?
4692- (rtu, uau, atu, cs) = login.get_config_urls("http://localhost:9876")
4693- self.assertEqual(rtu, "http://localhost:9876/rtu-localhost")
4694- self.assertEqual(uau, "http://localhost:9876/uau-localhost")
4695- self.assertEqual(atu, "http://localhost:9876/atu-localhost")
4696- self.assertEqual(cs, "foo-localhost")
4697-
4698- # is a URL specifically in the config correctly fetched?
4699- (rtu, uau, atu, cs) = login.get_config_urls("http://ubuntuone.com")
4700- self.assertEqual(rtu, "http://ubuntuone.com/rtu-ubuntuone")
4701- self.assertEqual(uau, "http://ubuntuone.com/uau-ubuntuone")
4702- self.assertEqual(atu, "http://ubuntuone.com/atu-ubuntuone")
4703- self.assertEqual(cs, "foo-ubuntuone")
4704-
4705- # is a URL not in the config correctly fetched as default?
4706- (rtu, uau, atu, cs) = login.get_config_urls("http://other.example.net")
4707- self.assertEqual(rtu, "http://other.example.net/rtu-default")
4708- self.assertEqual(uau, "http://other.example.net/uau-default")
4709- self.assertEqual(atu, "http://other.example.net/atu-default")
4710- self.assertEqual(cs, "foo-default")
4711-
4712-
4713 class BlockingSampleException(Exception):
4714 """The exception that will be thrown by the fake blocking."""
4715
4716@@ -439,34 +378,36 @@
4717 self.mockbusname.get_bus()
4718 self.mocker.result(mockbus)
4719 mockbus._register_object_path(ARGS)
4720+ self.mockprocessorclass = None
4721
4722- def ksc(k, v):
4723+ def ksc(k, val):
4724 """Assert over token and app_name."""
4725 self.assertEqual(k, APP_NAME)
4726- self.assertEqual(v, TOKEN)
4727+ self.assertEqual(val, TOKEN)
4728 self.keyring_was_set = True
4729- self.keyring_values = k, v
4730+ self.keyring_values = k, val
4731
4732 self.patch(ubuntu_sso.main, "keyring_store_credentials", ksc)
4733 self.keyring_was_set = False
4734+ self.keyring_values = None
4735
4736 def tearDown(self):
4737 """Verify the mocking bus and shut it down."""
4738 self.mocker.verify()
4739 self.mocker.restore()
4740
4741- def fake_ok_blocking(self, f, a, cb, eb):
4742+ def fake_ok_blocking(self, f, app, cb, eb):
4743 """A fake blocking function that succeeds."""
4744- cb(a, f())
4745+ cb(app, f())
4746
4747- def fake_err_blocking(self, f, a, cb, eb):
4748+ def fake_err_blocking(self, f, app, cb, eb):
4749 """A fake blocking function that fails."""
4750 try:
4751 f()
4752- except Exception, e:
4753- eb(a, except_to_errdict(e))
4754+ except Exception, e: # pylint: disable=W0703
4755+ eb(app, except_to_errdict(e))
4756 else:
4757- eb(a, except_to_errdict(BlockingSampleException()))
4758+ eb(app, except_to_errdict(BlockingSampleException()))
4759
4760 def test_creation(self):
4761 """Test that the object creation is successful."""
4762@@ -492,6 +433,7 @@
4763 self.mocker.replay()
4764
4765 def verify(app_name, result):
4766+ """The actual test."""
4767 self.assertEqual(result, expected_result)
4768 self.assertEqual(app_name, APP_NAME)
4769 d.callback(result)
4770@@ -514,6 +456,7 @@
4771 self.mocker.replay()
4772
4773 def verify(app_name, errdict):
4774+ """The actual test."""
4775 self.assertEqual(errdict["errtype"], "BlockingSampleException")
4776 self.assertEqual(app_name, APP_NAME)
4777 d.callback("Ok")
4778@@ -536,6 +479,7 @@
4779 self.mocker.replay()
4780
4781 def verify(app_name, result):
4782+ """The actual test."""
4783 self.assertEqual(result, expected_result)
4784 self.assertEqual(app_name, APP_NAME)
4785 d.callback(result)
4786@@ -559,6 +503,7 @@
4787 self.mocker.replay()
4788
4789 def verify(app_name, errdict):
4790+ """The actual test."""
4791 self.assertEqual(errdict["errtype"], "BlockingSampleException")
4792 self.assertEqual(app_name, APP_NAME)
4793 d.callback("Ok")
4794@@ -580,6 +525,7 @@
4795 self.mocker.replay()
4796
4797 def verify(app_name, result):
4798+ """The actual test."""
4799 self.assertEqual(result, EMAIL)
4800 self.assertEqual(app_name, APP_NAME)
4801 self.assertTrue(self.keyring_was_set, "The keyring should be set")
4802@@ -606,6 +552,7 @@
4803 self.mocker.replay()
4804
4805 def verify(app_name, errdict):
4806+ """The actual test."""
4807 self.assertEqual(app_name, APP_NAME)
4808 self.assertEqual(errdict["errtype"], "BlockingSampleException")
4809 self.assertFalse(self.keyring_was_set, "Keyring should not be set")
4810@@ -628,6 +575,7 @@
4811 self.mocker.replay()
4812
4813 def verify(app_name, result):
4814+ """The actual test."""
4815 self.assertEqual(result, EMAIL)
4816 self.assertEqual(app_name, APP_NAME)
4817 self.assertTrue(self.keyring_was_set, "The keyring should be set")
4818@@ -654,6 +602,7 @@
4819 self.mocker.replay()
4820
4821 def verify(app_name, errdict):
4822+ """The actual test."""
4823 self.assertEqual(app_name, APP_NAME)
4824 self.assertEqual(errdict["errtype"], "BlockingSampleException")
4825 self.assertFalse(self.keyring_was_set, "Keyring should not be set")
4826@@ -676,6 +625,7 @@
4827 self.mocker.replay()
4828
4829 def verify(app_name, result):
4830+ """The actual test."""
4831 self.assertEqual(result, EMAIL)
4832 self.assertEqual(app_name, APP_NAME)
4833 d.callback(result)
4834@@ -697,6 +647,7 @@
4835 self.mocker.replay()
4836
4837 def verify(app_name, errdict):
4838+ """The actual test."""
4839 self.assertEqual(errdict["errtype"], "BlockingSampleException")
4840 self.assertEqual(app_name, APP_NAME)
4841 d.callback("Ok")
4842@@ -718,6 +669,7 @@
4843 self.mocker.replay()
4844
4845 def verify(app_name, result):
4846+ """The actual test."""
4847 self.assertEqual(result, EMAIL)
4848 self.assertEqual(app_name, APP_NAME)
4849 d.callback(result)
4850@@ -741,6 +693,7 @@
4851 self.mocker.replay()
4852
4853 def verify(app_name, errdict):
4854+ """The actual test."""
4855 self.assertEqual(errdict["errtype"], "BlockingSampleException")
4856 self.assertEqual(app_name, APP_NAME)
4857 d.callback("Ok")
4858@@ -764,9 +717,11 @@
4859 expected_result = "expected result"
4860
4861 def f():
4862+ """No failure."""
4863 return expected_result
4864
4865 def verify(app_name, result):
4866+ """The actual test."""
4867 self.assertEqual(result, expected_result)
4868 self.assertEqual(app_name, APP_NAME)
4869 d.callback(result)
4870@@ -780,9 +735,11 @@
4871 expected_error_message = "expected error message"
4872
4873 def f():
4874+ """Failure."""
4875 raise BlockingSampleException(expected_error_message)
4876
4877 def verify(app_name, errdict):
4878+ """The actual test."""
4879 self.assertEqual(app_name, APP_NAME)
4880 self.assertEqual(errdict["errtype"], "BlockingSampleException")
4881 self.assertEqual(errdict["message"], expected_error_message)
4882@@ -846,7 +803,10 @@
4883
4884
4885 class KeyringCredentialsTestCase(MockerTestCase):
4886- """Checks the functions that access the keyring."""
4887+ """Check the functions that access the keyring."""
4888+
4889+ # Invalid name (should match ([a-z_][a-z0-9_]*|[A-Z_][A-Z0-9_]*)$)
4890+ # pylint: disable=C0103
4891
4892 def test_keyring_store_cred(self):
4893 """Verify the method that stores credentials."""
4894@@ -894,6 +854,9 @@
4895 class CredentialsTestCase(TestCase, MockerTestCase):
4896 """Tests for the credentials related DBus methods."""
4897
4898+ # Invalid name (should match ([a-z_][a-z0-9_]*|[A-Z_][A-Z0-9_]*)$)
4899+ # pylint: disable=C0103
4900+
4901 timeout = 5
4902
4903 def test_find_credentials(self):
4904@@ -926,6 +889,7 @@
4905 d = Deferred()
4906
4907 def verify(app_name, credentials):
4908+ """The actual test."""
4909 self.assertEqual(credentials, expected_creds)
4910 d.callback("ok")
4911
4912@@ -945,6 +909,7 @@
4913 d = Deferred()
4914
4915 def verify(result, *a):
4916+ """The actual test."""
4917 self.assertEqual(result, APP_NAME)
4918 d.callback("ok")
4919
4920@@ -966,6 +931,7 @@
4921 d = Deferred()
4922
4923 def verify(app_name, error_message, detailed_error):
4924+ """The actual test."""
4925 self.assertEqual(app_name, APP_NAME)
4926 d.callback("ok")
4927
4928@@ -987,6 +953,7 @@
4929 d = Deferred()
4930
4931 def verify(app_name, credentials):
4932+ """The actual test."""
4933 self.assertEqual(credentials, expected_creds)
4934 d.callback("ok")
4935
4936@@ -1006,6 +973,7 @@
4937 d = Deferred()
4938
4939 def verify(result, *a):
4940+ """The actual test."""
4941 self.assertEqual(result, APP_NAME)
4942 d.callback("ok")
4943
4944@@ -1027,6 +995,7 @@
4945 d = Deferred()
4946
4947 def verify(app_name, error_message, detailed_error):
4948+ """The actual test."""
4949 self.assertEqual(app_name, APP_NAME)
4950 d.callback("ok")
4951
4952@@ -1071,6 +1040,7 @@
4953 d = Deferred()
4954
4955 def verify(app_name, error_message, detailed_error):
4956+ """The actual test."""
4957 self.assertEqual(app_name, APP_NAME)
4958 d.callback("ok")
4959
4960@@ -1090,6 +1060,7 @@
4961 self.mocker.replay()
4962
4963 def verify(app_name, msg, full_error):
4964+ """The actual test."""
4965 self.assertEqual(app_name, APP_NAME)
4966 d.callback("ok")
4967
4968@@ -1109,6 +1080,7 @@
4969 self.mocker.replay()
4970
4971 def verify(app_name, credentials):
4972+ """The actual test."""
4973 self.assertEqual(credentials, expected_creds)
4974 d.callback("ok")
4975
4976@@ -1127,6 +1099,7 @@
4977 self.mocker.replay()
4978
4979 def verify(app_name, msg, full_error):
4980+ """The actual test."""
4981 self.assertEqual(app_name, APP_NAME)
4982 d.callback("ok")
4983
4984@@ -1142,6 +1115,7 @@
4985 self.mocker.replay()
4986
4987 def verify(app_name):
4988+ """The actual test."""
4989 self.assertEqual(app_name, APP_NAME)
4990 d.callback("ok")
4991
4992@@ -1157,6 +1131,9 @@
4993
4994 def setUp(self):
4995 """Init."""
4996+ self.args = None
4997+ self.kwargs = None
4998+
4999 idle_add = lambda f, *args, **kwargs: f(*args, **kwargs)
5000 self.patch(gobject, "idle_add", idle_add)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: