Merge lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-0.99.6 into lp:ubuntu/maverick/ubuntu-sso-client
- Maverick (10.10)
- ubuntu-sso-client-0.99.6
- Merge into maverick
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ken VanDine | Approve | ||
Review via email: mp+34920@code.launchpad.net |
Commit message
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.
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' |
3231 | Binary 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.
Approve!