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

Proposed by Natalia Bidart
Status: Superseded
Proposed branch: lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-1.0.9
Merge into: lp:ubuntu/maverick/ubuntu-sso-client
Diff against target: 1307 lines (+490/-147)
12 files modified
PKG-INFO (+1/-1)
bin/ubuntu-sso-login (+11/-17)
data/ui.glade (+40/-32)
debian/changelog (+61/-0)
debian/control (+2/-1)
debian/watch (+1/-1)
run-tests (+1/-1)
setup.py (+3/-2)
ubuntu_sso/gui.py (+33/-22)
ubuntu_sso/main.py (+108/-39)
ubuntu_sso/tests/test_gui.py (+65/-20)
ubuntu_sso/tests/test_main.py (+164/-11)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-1.0.9
Reviewer Review Type Date Requested Status
Ubuntu Sponsors Pending
Review via email: mp+57379@code.launchpad.net

Description of the change

  * New upstream release:
    [ Natalia B. Bidart <email address hidden> ]
      - Register now uses the 'displayname' field to pass it on to SSO as display name (LP: #709494).

To post a comment you must log in.

Unmerged revisions

22. By Natalia Bidart

Attaching bug Bug #709494.

21. By Natalia Bidart

* New upstream release:
  [ Natalia B. Bidart <email address hidden> ]
    - Register now uses the 'displayname' field to pass it on to SSO as
    display name (LP: #709494).

20. By Natalia Bidart

* New upstream release.

  [ Natalia B. Bidart <email address hidden> ]
     * Avoid generating an extra token when attempting to validate a user
     account (LP: #687523).

19. By Sebastien Bacher

releasing version 1.0.7-0ubuntu1

18. By Natalia Bidart

* New upstream release:

[ Alejandro J. Cura <email address hidden> ]
  * Replace twisted gtk reactor with the standard gtk mainloop. (LP: #655327).

[ Alejandro J. Cura <email address hidden> ]
  * Call the dbus mainloop thread init (fixes LP: #656545).

* Adding .bzr-builddeb/default.conf as per Michael Vog (mvo) request.

* Adding dpkg (>= 1.15.7.2) as Pre-Depends (fixes LP: #658768).

* Adding gnome-keyring as dep since python-gnomekeyring doesn't install it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'PKG-INFO'
2--- PKG-INFO 2010-10-01 14:57:22 +0000
3+++ PKG-INFO 2011-04-12 19:01:16 +0000
4@@ -1,6 +1,6 @@
5 Metadata-Version: 1.1
6 Name: ubuntu-sso-client
7-Version: 1.0.3
8+Version: 1.0.9
9 Summary: Ubuntu Single Sign-On client
10 Home-page: https://launchpad.net/ubuntu-sso-client
11 Author: Natalia Bidart
12
13=== modified file 'bin/ubuntu-sso-login'
14--- bin/ubuntu-sso-login 2010-10-01 14:57:22 +0000
15+++ bin/ubuntu-sso-login 2011-04-12 19:01:16 +0000
16@@ -42,6 +42,7 @@
17 import signal
18 import sys
19
20+import dbus.mainloop.glib
21 import dbus.service
22 import gtk
23
24@@ -57,21 +58,21 @@
25
26
27 logger = setup_logging("ubuntu-sso-login")
28+dbus.mainloop.glib.threads_init()
29 gtk.gdk.threads_init()
30 DBusGMainLoop(set_as_default=True)
31
32
33 def sighup_handler(*a, **kw):
34- """Stop the service.
35-
36- This is not thread safe, see the link below for info:
37- www.listware.net/201004/gtk-devel-list/115067-unix-signals-in-glib.html
38- """
39- from twisted.internet import reactor
40- # Module 'twisted.internet.reactor' has no 'stop' member
41- # pylint: disable=E1101
42+ """Stop the service."""
43+ # This handler may be called in any thread, so is not thread safe.
44+ # See the link below for info:
45+ # www.listware.net/201004/gtk-devel-list/115067-unix-signals-in-glib.html
46+ #
47+ # gtk.main_quit and the logger methods are safe to be called from any thread.
48+ # Just don't call other random stuff here.
49 logger.info("Stoping Ubuntu SSO login manager since SIGHUP was received.")
50- reactor.stop()
51+ gtk.main_quit()
52
53
54 if __name__ == "__main__":
55@@ -83,10 +84,6 @@
56 logger.error("Ubuntu SSO login manager already running, quitting.")
57 sys.exit(0)
58
59- logger.debug("Installing the Twisted gtk2reactor.")
60- from twisted.internet import gtk2reactor
61- gtk2reactor.install()
62-
63 logger.debug("Hooking up SIGHUP with handler %r.", sighup_handler)
64 signal.signal(signal.SIGHUP, sighup_handler)
65
66@@ -97,7 +94,4 @@
67 bus=dbus.SessionBus()),
68 object_path=DBUS_CRED_PATH)
69
70- from twisted.internet import reactor
71- # Module 'twisted.internet.reactor' has no 'run' member
72- # pylint: disable=E1101
73- reactor.run()
74+ gtk.main()
75
76=== modified file 'data/ui.glade'
77--- data/ui.glade 2010-08-27 22:16:40 +0000
78+++ data/ui.glade 2011-04-12 19:01:16 +0000
79@@ -264,36 +264,62 @@
80 </packing>
81 </child>
82 <child>
83- <object class="GtkHButtonBox" id="hbuttonbox1">
84+ <object class="GtkHBox" id="hbox2">
85 <property name="visible">True</property>
86- <property name="spacing">5</property>
87- <property name="layout_style">end</property>
88 <child>
89- <object class="GtkButton" id="join_cancel_button">
90- <property name="label">gtk-cancel</property>
91+ <object class="GtkLinkButton" id="login_button">
92+ <property name="label">login button</property>
93 <property name="visible">True</property>
94 <property name="can_focus">True</property>
95 <property name="receives_default">True</property>
96- <property name="use_stock">True</property>
97+ <property name="relief">none</property>
98+ <signal name="clicked" handler="on_sign_in_button_clicked" swapped="no"/>
99 </object>
100 <packing>
101 <property name="expand">False</property>
102- <property name="fill">False</property>
103+ <property name="fill">True</property>
104 <property name="position">0</property>
105 </packing>
106 </child>
107 <child>
108- <object class="GtkButton" id="join_ok_button">
109- <property name="label">gtk-go-forward</property>
110+ <object class="GtkHButtonBox" id="hbuttonbox1">
111 <property name="visible">True</property>
112- <property name="can_focus">True</property>
113- <property name="receives_default">True</property>
114- <property name="use_stock">True</property>
115- <signal name="clicked" handler="on_join_ok_button_clicked"/>
116+ <property name="can_focus">False</property>
117+ <property name="spacing">5</property>
118+ <property name="layout_style">end</property>
119+ <child>
120+ <object class="GtkButton" id="join_cancel_button">
121+ <property name="label">gtk-cancel</property>
122+ <property name="visible">True</property>
123+ <property name="can_focus">True</property>
124+ <property name="receives_default">True</property>
125+ <property name="use_stock">True</property>
126+ </object>
127+ <packing>
128+ <property name="expand">False</property>
129+ <property name="fill">False</property>
130+ <property name="position">0</property>
131+ </packing>
132+ </child>
133+ <child>
134+ <object class="GtkButton" id="join_ok_button">
135+ <property name="label">gtk-go-forward</property>
136+ <property name="visible">True</property>
137+ <property name="can_focus">True</property>
138+ <property name="receives_default">True</property>
139+ <property name="use_stock">True</property>
140+ <signal name="clicked" handler="on_join_ok_button_clicked" swapped="no"/>
141+ </object>
142+ <packing>
143+ <property name="expand">False</property>
144+ <property name="fill">False</property>
145+ <property name="position">1</property>
146+ </packing>
147+ </child>
148 </object>
149 <packing>
150 <property name="expand">False</property>
151- <property name="fill">False</property>
152+ <property name="pack_type">end</property>
153 <property name="position">1</property>
154 </packing>
155 </child>
156@@ -303,24 +329,6 @@
157 <property name="position">12</property>
158 </packing>
159 </child>
160- <child>
161- <object class="GtkLinkButton" id="login_button">
162- <property name="label" translatable="yes">login button</property>
163- <property name="visible">True</property>
164- <property name="can_focus">True</property>
165- <property name="receives_default">True</property>
166- <property name="has_tooltip">True</property>
167- <property name="relief">none</property>
168- <signal name="clicked" handler="on_sign_in_button_clicked"/>
169- </object>
170- <packing>
171- <property name="expand">False</property>
172- <property name="position">13</property>
173- </packing>
174- </child>
175- <child>
176- <placeholder/>
177- </child>
178 </object>
179 <object class="GtkVBox" id="processing_vbox">
180 <property name="visible">True</property>
181
182=== modified file 'debian/changelog'
183--- debian/changelog 2010-10-01 15:35:43 +0000
184+++ debian/changelog 2011-04-12 19:01:16 +0000
185@@ -1,3 +1,64 @@
186+ubuntu-sso-client (1.0.9-0ubuntu1) UNRELEASED; urgency=low
187+
188+ * New upstream release:
189+
190+ [ Natalia B. Bidart <natalia.bidart@canonical.com> ]
191+ - Register now uses the 'displayname' field to pass it on to SSO as
192+ display name (LP: #709494).
193+
194+ -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Tue, 29 Mar 2011 16:26:42 -0300
195+
196+ubuntu-sso-client (1.0.8-0ubuntu1) maverick-proposed; urgency=low
197+
198+ * New upstream release.
199+
200+ [ Natalia B. Bidart <natalia.bidart@canonical.com> ]
201+ * Avoid generating an extra token when attempting to validate a user
202+ account (LP: #687523).
203+
204+ -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Thu, 16 Dec 2010 13:19:01 -0300
205+
206+ubuntu-sso-client (1.0.7-0ubuntu1) maverick-proposed; urgency=low
207+
208+ * New upstream release (1.0.6, 1.0.7):
209+
210+ [ Natalia B. Bidart <natalia.bidart@canonical.com> ]
211+ * Added a new DBus signal UserNotValidated to indicate when a user is
212+ registered but not validated (LP: #667899).
213+ * Added new workflow so email validation is requested if necessary.
214+ * The verify email page should be always built, not only on registration.
215+
216+ [ Alejandro J. Cura <alecu@canonical.com> ]
217+ * Store credentials on the keyring *only* from the main thread (LP:
218+ #656545).
219+
220+ * New upstream release (1.0.5):
221+
222+ [ Natalia B. Bidart <natalia.bidart@canonical.com> ]
223+
224+ * Credentials are removed if the pinging to the server fails or any
225+ other exception occurs (LP: #660516).
226+
227+ -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Thu, 04 Nov 2010 09:21:00 -0300
228+
229+ubuntu-sso-client (1.0.4-0ubuntu1) maverick-proposed; urgency=low
230+
231+ * New upstream release:
232+
233+ [ Alejandro J. Cura <alecu@canonical.com> ]
234+ * Replace twisted gtk reactor with the standard gtk mainloop. (LP: #655327).
235+
236+ [ Alejandro J. Cura <alecu@canonical.com> ]
237+ * Call the dbus mainloop thread init (fixes LP: #656545).
238+
239+ * Adding .bzr-builddeb/default.conf as per Michael Vog (mvo) request.
240+
241+ * Adding dpkg (>= 1.15.7.2) as Pre-Depends (fixes LP: #658768).
242+
243+ * Adding gnome-keyring as dep since python-gnomekeyring doesn't install it.
244+
245+ -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Tue, 12 Oct 2010 10:07:55 -0300
246+
247 ubuntu-sso-client (1.0.3-0ubuntu1) maverick; urgency=low
248
249 * New upstream release:
250
251=== modified file 'debian/control'
252--- debian/control 2010-09-13 15:02:42 +0000
253+++ debian/control 2011-04-12 19:01:16 +0000
254@@ -13,9 +13,10 @@
255 Package: ubuntu-sso-client
256 Architecture: all
257 XB-Python-Version: ${python:Versions}
258+Pre-Depends: dpkg (>=1.15.7.2),
259 Depends: ${misc:Depends},
260 ${python:Depends},
261- dpkg (>=1.15.7.2),
262+ gnome-keyring,
263 python-dbus,
264 python-gnomekeyring,
265 python-gtk2,
266
267=== modified file 'debian/watch'
268--- debian/watch 2010-09-22 18:53:18 +0000
269+++ debian/watch 2011-04-12 19:01:16 +0000
270@@ -1,3 +1,3 @@
271 version=3
272-http://launchpad.net/ubuntu-sso-client/+download?start=10 .*/ubuntu-sso-client-([0-9.]+)\.tar\.gz
273+http://launchpad.net/ubuntu-sso-client/+download?start=20 .*/ubuntu-sso-client-([0-9.]+)\.tar\.gz
274
275
276=== modified file 'run-tests'
277--- run-tests 2010-09-08 19:25:02 +0000
278+++ run-tests 2011-04-12 19:01:16 +0000
279@@ -16,7 +16,7 @@
280 # with this program. If not, see <http://www.gnu.org/licenses/>.
281
282 `which xvfb-run` ./contrib/test "$@"
283-pylint contrib ubuntu_sso
284+pylint ubuntu_sso
285 if [ -x `which pep8` ]; then
286 pep8 --repeat bin/ contrib/ ubuntu_sso/
287 else
288
289=== modified file 'setup.py'
290--- setup.py 2010-10-01 14:57:22 +0000
291+++ setup.py 2011-04-12 19:01:16 +0000
292@@ -36,7 +36,8 @@
293 from distutils.command import clean, build
294
295 # Defining variables for various rules here, similar to a Makefile.am
296-CLEANFILES = ['data/com.ubuntu.sso.service']
297+CLEANFILES = ['data/com.ubuntu.sso.service', 'po/ubuntu-sso-client.pot',
298+ 'MANIFEST']
299
300
301 # XXX: This needs some serious cleanup
302@@ -86,7 +87,7 @@
303
304 DistUtilsExtra.auto.setup(
305 name='ubuntu-sso-client',
306- version='1.0.3',
307+ version='1.0.9',
308 license='GPL v3',
309 author='Natalia Bidart',
310 author_email='natalia.bidart@canonical.com',
311
312=== modified file 'ubuntu_sso/gui.py'
313--- ubuntu_sso/gui.py 2010-10-01 14:57:22 +0000
314+++ ubuntu_sso/gui.py 2011-04-12 19:01:16 +0000
315@@ -28,7 +28,7 @@
316 import dbus
317 import gettext
318 import gobject
319-import gtk
320+import gtk # pylint: disable=W0403
321 import webkit
322 import xdg
323
324@@ -125,7 +125,7 @@
325 self.is_password = is_password
326 self.warning = None
327
328- super(LabeledEntry, self).__init__(*args, **kwargs)
329+ gtk.Entry.__init__(self, *args, **kwargs)
330
331 self.set_width_chars(DEFAULT_WIDTH)
332 self._set_label(self, None)
333@@ -161,7 +161,7 @@
334
335 def get_text(self):
336 """Get text only if it's not the label nor empty."""
337- result = super(LabeledEntry, self).get_text()
338+ result = gtk.Entry.get_text(self)
339 if result == self.label or result.isspace():
340 result = ''
341 return result
342@@ -252,6 +252,8 @@
343 self.tc_uri = tc_uri
344 self.help_text = help_text
345 self.close_callback = close_callback
346+ self.user_email = None
347+ self.user_password = None
348
349 ui_filename = get_data_file('ui.glade')
350 builder = gtk.Builder()
351@@ -319,13 +321,13 @@
352 self._append_page(self._build_login_page())
353 self._append_page(self._build_request_password_token_page())
354 self._append_page(self._build_set_new_password_page())
355+ self._append_page(self._build_verify_email_page())
356
357 window_size = None
358 if not login_only:
359 window_size = (550, 500)
360 self._append_page(self._build_enter_details_page())
361 self._append_page(self._build_tc_page())
362- self._append_page(self._build_verify_email_page())
363 self.login_button.grab_focus()
364 self._set_current_page(self.enter_details_vbox)
365 else:
366@@ -357,6 +359,8 @@
367 self._filter_by_app_name(self.on_logged_in),
368 'LoginError':
369 self._filter_by_app_name(self.on_login_error),
370+ 'UserNotValidated':
371+ self._filter_by_app_name(self.on_user_not_validated),
372 'PasswordResetTokenSent':
373 self._filter_by_app_name(self.on_password_reset_token_sent),
374 'PasswordResetError':
375@@ -383,8 +387,6 @@
376 msg = 'UbuntuSSOClientGUI: failed set_transient_for win id %r'
377 logger.exception(msg, window_id)
378
379- # Hidding unused widgets to save some space (LP #627440).
380- self.name_entry.hide()
381 self.yes_to_updates_checkbutton.hide()
382
383 self.window.show()
384@@ -417,8 +419,8 @@
385
386 match = self.bus.add_signal_receiver(method, signal_name=signal,
387 dbus_interface=iface)
388- logger.info('Connecting signal %r with method %r at iface %r.' \
389- 'Match: %r', signal, method, iface, match)
390+ logger.debug('Connecting signal %r with method %r at iface %r.' \
391+ 'Match: %r', signal, method, iface, match)
392 self._signals_receivers[(iface, signal)] = method
393
394 def _debug(self, *args, **kwargs):
395@@ -725,8 +727,8 @@
396 remove = self.bus.remove_signal_receiver
397 for (iface, signal) in self._signals_receivers.keys():
398 method = self._signals_receivers.pop((iface, signal))
399- logger.info('Removing signal %r with method %r at iface %r.',
400- signal, method, iface)
401+ logger.debug('Removing signal %r with method %r at iface %r.',
402+ signal, method, iface)
403 remove(method, signal_name=signal, dbus_interface=iface)
404
405 # hide the main window
406@@ -765,11 +767,10 @@
407
408 error = False
409
410- # Hidding unused widgets to save some space (LP #627440).
411- #name = self.name_entry.get_text()
412- #if not name:
413- # self.name_entry.set_warning(self.FIELD_REQUIRED)
414- # error = True
415+ name = self.name_entry.get_text()
416+ if not name:
417+ self.name_entry.set_warning(self.FIELD_REQUIRED)
418+ error = True
419
420 # check email
421 email1 = self.email1_entry.get_text()
422@@ -804,12 +805,15 @@
423 return
424
425 self._set_current_page(self.processing_vbox)
426+ self.user_email = email1
427+ self.user_password = password1
428
429- logger.info('Calling register_user with email %r, password <hidden>,' \
430- ' captcha_id %r and captcha_solution %r.', email1,
431- self._captcha_id, captcha_solution)
432- f = self.backend.register_user
433- f(self.app_name, email1, password1, self._captcha_id, captcha_solution,
434+ logger.info('Calling register_with_name with email %r, password, '
435+ '<hidden> name %r, captcha_id %r and captcha_solution %r.',
436+ email1, name, self._captcha_id, captcha_solution)
437+ f = self.backend.register_with_name
438+ f(self.app_name, email1, password1, name,
439+ self._captcha_id, captcha_solution,
440 reply_handler=NO_OP, error_handler=NO_OP)
441
442 def on_tc_button_clicked(self, *args, **kwargs):
443@@ -832,8 +836,8 @@
444 self.email_token_entry.set_warning(self.FIELD_REQUIRED)
445 return
446
447- email = self.email1_entry.get_text()
448- password = self.password1_entry.get_text()
449+ email = self.user_email
450+ password = self.user_password
451 f = self.backend.validate_email
452 logger.info('Calling validate_email with email %r, password <hidden>' \
453 ', app_name %r and email_token %r.', email, self.app_name,
454@@ -871,6 +875,8 @@
455 reply_handler=NO_OP, error_handler=NO_OP)
456
457 self._set_current_page(self.processing_vbox)
458+ self.user_email = email
459+ self.user_password = password
460
461 def on_login_back_button_clicked(self, *args, **kwargs):
462 """User wants to go to the previous page."""
463@@ -1061,6 +1067,11 @@
464 msg))
465
466 @log_call
467+ def on_user_not_validated(self, app_name, email, *args, **kwargs):
468+ """User was not validated."""
469+ self.on_user_registered(app_name, email)
470+
471+ @log_call
472 def on_password_reset_token_sent(self, app_name, email, *args, **kwargs):
473 """Password reset token was successfully sent."""
474 msg = self.SET_NEW_PASSWORD_LABEL % {'email': email}
475
476=== modified file 'ubuntu_sso/main.py'
477--- ubuntu_sso/main.py 2010-09-14 19:28:09 +0000
478+++ ubuntu_sso/main.py 2011-04-12 19:01:16 +0000
479@@ -55,6 +55,7 @@
480 logger = setup_logging("ubuntu_sso.main")
481 PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/"
482 SERVICE_URL = "https://login.ubuntu.com/api/1.0"
483+NO_OP = lambda *args, **kwargs: None
484
485
486 class NoDefaultConfigError(Exception):
487@@ -96,10 +97,16 @@
488 """The new password could not be set."""
489
490
491-def keyring_store_credentials(app_name, credentials):
492+def keyring_store_credentials(app_name, credentials, callback, *cb_args):
493 """Store the credentials in the keyring."""
494- logger.info('keyring_store_credentials: app_name "%s".', app_name)
495- Keyring(app_name).set_ubuntusso_attr(credentials)
496+
497+ def _inner():
498+ """Store the credentials, and trigger the callback."""
499+ logger.info('keyring_store_credentials: app_name "%s".', app_name)
500+ Keyring(app_name).set_ubuntusso_attr(credentials)
501+ callback(*cb_args)
502+
503+ gobject.idle_add(_inner)
504
505
506 def keyring_get_credentials(app_name):
507@@ -164,24 +171,27 @@
508
509 return captcha['captcha_id']
510
511- def register_user(self, email, password, captcha_id, captcha_solution):
512+ def register_with_name(self, email, password, displayname,
513+ captcha_id, captcha_solution):
514 """Register a new user with 'email' and 'password'."""
515- logger.debug('register_user: email: %r password: <hidden>, '
516- 'captcha_id: %r, captcha_solution: %r',
517- email, captcha_id, captcha_solution)
518+ logger.debug('register_with_name: email: %r password: <hidden>, '
519+ 'displayname: %r, captcha_id: %r, captcha_solution: %r',
520+ email, displayname, captcha_id, captcha_solution)
521 sso_service = self.sso_service_class(None, self.service_url)
522 if not self._valid_email(email):
523- logger.error('register_user: InvalidEmailError for email: %r',
524+ logger.error('register_with_name: InvalidEmailError for email: %r',
525 email)
526 raise InvalidEmailError()
527 if not self._valid_password(password):
528- logger.error('register_user: InvalidPasswordError')
529+ logger.error('register_with_name: InvalidPasswordError')
530 raise InvalidPasswordError()
531
532 result = sso_service.registrations.register(
533- email=email, password=password, captcha_id=captcha_id,
534+ email=email, password=password,
535+ displayname=displayname,
536+ captcha_id=captcha_id,
537 captcha_solution=captcha_solution)
538- logger.info('register_user: email: %r result: %r', email, result)
539+ logger.info('register_with_name: email: %r result: %r', email, result)
540
541 if result['status'] == 'error':
542 errorsdict = self._format_webservice_errors(result['errors'])
543@@ -191,6 +201,17 @@
544 else:
545 return email
546
547+ def register_user(self, email, password,
548+ captcha_id, captcha_solution):
549+ """Register a new user with 'email' and 'password'."""
550+ logger.debug('register_user: email: %r password: <hidden>, '
551+ 'captcha_id: %r, captcha_solution: %r',
552+ email, captcha_id, captcha_solution)
553+ res = self.register_with_name(email, password, displayname='',
554+ captcha_id=captcha_id,
555+ captcha_solution=captcha_solution)
556+ return res
557+
558 def login(self, email, password, token_name):
559 """Login a user with 'email' and 'password'."""
560 logger.debug('login: email: %r password: <hidden>, token_name: %r',
561@@ -209,6 +230,25 @@
562 'token_name: %r', credentials['consumer_key'], token_name)
563 return credentials
564
565+ def is_validated(self, token, sso_service=None):
566+ """Return if user with 'email' and 'password' is validated."""
567+ logger.debug('is_validated: requesting accounts.me() info.')
568+ if sso_service is None:
569+ oauth_token = oauth.OAuthToken(token['token'],
570+ token['token_secret'])
571+ authorizer = OAuthAuthorizer(token['consumer_key'],
572+ token['consumer_secret'],
573+ oauth_token)
574+ sso_service = self.sso_service_class(authorizer, self.service_url)
575+
576+ me_info = sso_service.accounts.me()
577+ key = 'preferred_email'
578+ result = key in me_info and me_info[key] != None
579+
580+ logger.info('is_validated: consumer_key: %r, result: %r.',
581+ token['consumer_key'], result)
582+ return result
583+
584 def validate_email(self, email, password, email_token, token_name):
585 """Validate an email token for user with 'email' and 'password'."""
586 logger.debug('validate_email: email: %r password: <hidden>, '
587@@ -311,12 +351,8 @@
588 dbus.service.Object.__init__(self, object_path="/sso",
589 bus_name=bus_name)
590 self.sso_login_processor_class = sso_login_processor_class
591- self.sso_service_class = sso_service_class
592-
593- def processor(self):
594- """Create a login processor with the given class and service class."""
595- return self.sso_login_processor_class(
596- sso_service_class=self.sso_service_class)
597+ self.processor = self.sso_login_processor_class(
598+ sso_service_class=sso_service_class)
599
600 # generate_capcha signals
601 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
602@@ -337,7 +373,7 @@
603 """Call the matching method in the processor."""
604 def f():
605 """Inner function that will be run in a thread."""
606- return self.processor().generate_captcha(filename)
607+ return self.processor.generate_captcha(filename)
608 blocking(f, app_name, self.CaptchaGenerated,
609 self.CaptchaGenerationError)
610
611@@ -361,8 +397,19 @@
612 """Call the matching method in the processor."""
613 def f():
614 """Inner function that will be run in a thread."""
615- return self.processor().register_user(email, password,
616- captcha_id, captcha_solution)
617+ return self.processor.register_user(email, password,
618+ captcha_id, captcha_solution)
619+ blocking(f, app_name, self.UserRegistered, self.UserRegistrationError)
620+
621+ @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
622+ in_signature='ssssss')
623+ def register_with_name(self, app_name, email, password, name,
624+ captcha_id, captcha_solution):
625+ """Call the matching method in the processor."""
626+ def f():
627+ """Inner function that will be run in a thread."""
628+ return self.processor.register_with_name(email, password, name,
629+ captcha_id, captcha_solution)
630 blocking(f, app_name, self.UserRegistered, self.UserRegistrationError)
631
632 # login signals
633@@ -378,6 +425,12 @@
634 logger.debug('SSOLogin: emitting LoginError with '
635 'app_name "%s" and error %r', app_name, error)
636
637+ @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
638+ def UserNotValidated(self, app_name, result):
639+ """Signal thrown when the user is not validated."""
640+ logger.debug('SSOLogin: emitting UserNotValidated with app_name "%s" '
641+ 'and result %r', app_name, result)
642+
643 @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME,
644 in_signature='sss')
645 def login(self, app_name, email, password):
646@@ -387,13 +440,21 @@
647 token_name = get_token_name(app_name)
648 logger.debug('login: token_name %r, email %r, password <hidden>.',
649 token_name, email)
650- credentials = self.processor().login(email, password, token_name)
651+ credentials = self.processor.login(email, password, token_name)
652 logger.debug('login returned not None credentials? %r.',
653 credentials is not None)
654- assert credentials is not None
655- keyring_store_credentials(app_name, credentials)
656- return email
657- blocking(f, app_name, self.LoggedIn, self.LoginError)
658+ return credentials
659+
660+ def success_cb(app_name, credentials):
661+ """Login finished successfull."""
662+ is_validated = self.processor.is_validated(credentials)
663+ logger.debug('user is validated? %r.', is_validated)
664+ if is_validated:
665+ keyring_store_credentials(app_name, credentials,
666+ self.LoggedIn, app_name, email)
667+ else:
668+ self.UserNotValidated(app_name, email)
669+ blocking(f, app_name, success_cb, self.LoginError)
670
671 # validate_email signals
672 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
673@@ -415,11 +476,16 @@
674 def f():
675 """Inner function that will be run in a thread."""
676 token_name = get_token_name(app_name)
677- credentials = self.processor().validate_email(email, password,
678- email_token, token_name)
679- keyring_store_credentials(app_name, credentials)
680- return email
681- blocking(f, app_name, self.EmailValidated, self.EmailValidationError)
682+ credentials = self.processor.validate_email(email, password,
683+ email_token, token_name)
684+
685+ def _email_stored():
686+ """The email was stored, so call the signal."""
687+ self.EmailValidated(app_name, email)
688+
689+ keyring_store_credentials(app_name, credentials, _email_stored)
690+
691+ blocking(f, app_name, NO_OP, self.EmailValidationError)
692
693 # request_password_reset_token signals
694 @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss")
695@@ -440,7 +506,7 @@
696 """Call the matching method in the processor."""
697 def f():
698 """Inner function that will be run in a thread."""
699- return self.processor().request_password_reset_token(email)
700+ return self.processor.request_password_reset_token(email)
701 blocking(f, app_name, self.PasswordResetTokenSent,
702 self.PasswordResetError)
703
704@@ -463,8 +529,8 @@
705 """Call the matching method in the processor."""
706 def f():
707 """Inner function that will be run in a thread."""
708- return self.processor().set_new_password(email, token,
709- new_password)
710+ return self.processor.set_new_password(email, token,
711+ new_password)
712 blocking(f, app_name, self.PasswordChanged, self.PasswordChangeError)
713
714
715@@ -481,19 +547,19 @@
716 @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="s")
717 def AuthorizationDenied(self, app_name):
718 """Signal thrown when the user denies the authorization."""
719- logger.info('SSOLogin: emitting AuthorizationDenied with app_name '
720- '"%s"', app_name)
721+ logger.info('SSOCredentials: emitting AuthorizationDenied with '
722+ 'app_name "%s"', app_name)
723
724 @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sa{ss}")
725 def CredentialsFound(self, app_name, credentials):
726 """Signal thrown when the credentials are found."""
727- logger.info('SSOLogin: emitting CredentialsFound with app_name "%s"',
728- app_name)
729+ logger.info('SSOCredentials: emitting CredentialsFound with '
730+ 'app_name "%s"', app_name)
731
732 @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sss")
733 def CredentialsError(self, app_name, error_message, detailed_error):
734 """Signal thrown when there is a problem finding the credentials."""
735- logger.debug('SSOCredentials: emitting CredentialsError with app_name '
736+ logger.error('SSOCredentials: emitting CredentialsError with app_name '
737 '"%s" and error_message %r', app_name, error_message)
738
739 @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME,
740@@ -510,7 +576,8 @@
741
742 def _login_success_cb(self, dialog, app_name, email):
743 """Handles the response from the UI dialog."""
744- logger.info('Login successful for app %r, email %r', app_name, email)
745+ logger.info('Login successful for app %r, email %r. Still pending to '
746+ 'ping server and send result signal.', app_name, email)
747 try:
748 creds = keyring_get_credentials(app_name)
749 self._ping_url(app_name, email, creds)
750@@ -518,6 +585,7 @@
751 except: # pylint: disable=W0702
752 msg = "Problem getting the credentials from the keyring."
753 logger.exception(msg)
754+ self.clear_token(app_name)
755 self.CredentialsError(app_name, msg, traceback.format_exc())
756
757 def _login_error_cb(self, dialog, app_name, error):
758@@ -639,6 +707,7 @@
759
760 'app_name' is the name of the application.
761 """
762+ logger.info('Clearing credentials for app %r.', app_name)
763 try:
764 creds = Keyring(app_name)
765 creds.delete_ubuntusso_attr()
766
767=== modified file 'ubuntu_sso/tests/test_gui.py'
768--- ubuntu_sso/tests/test_gui.py 2010-09-08 19:25:02 +0000
769+++ ubuntu_sso/tests/test_gui.py 2011-04-12 19:01:16 +0000
770@@ -63,7 +63,7 @@
771 self._args = args
772 self._kwargs = kwargs
773 self._called = {}
774- for i in ('generate_captcha', 'login', 'register_user',
775+ for i in ('generate_captcha', 'login', 'register_with_name',
776 'validate_email', 'request_password_reset_token',
777 'set_new_password'):
778 setattr(self, i, self._record_call(i))
779@@ -674,11 +674,11 @@
780 """Clicking 'join_ok_button' sends info to backend using 'register'."""
781 self.click_join_with_valid_data()
782
783- # assert register_user was called
784- expected = 'register_user'
785+ # assert register_with_name was called
786+ expected = 'register_with_name'
787 self.assertIn(expected, self.ui.backend._called)
788 self.assertEqual(self.ui.backend._called[expected],
789- ((APP_NAME, EMAIL, PASSWORD, CAPTCHA_ID,
790+ ((APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID,
791 CAPTCHA_SOLUTION),
792 dict(reply_handler=gui.NO_OP,
793 error_handler=gui.NO_OP)))
794@@ -856,6 +856,12 @@
795 self.ui.join_ok_button.clicked()
796 self.assertTrue(self._called)
797
798+ def test_user_and_pass_are_cached(self):
799+ """Username and password are temporarly cached for further use."""
800+ self.click_join_with_valid_data()
801+ self.assertEqual(self.ui.user_email, EMAIL)
802+ self.assertEqual(self.ui.user_password, PASSWORD)
803+
804
805 class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase):
806 """Test suite for the user registration (with no t&c link)."""
807@@ -1005,6 +1011,17 @@
808 dict(reply_handler=gui.NO_OP,
809 error_handler=gui.NO_OP)))
810
811+ def test_on_verify_token_button_clicked(self):
812+ """Verify token uses cached user_email and user_password."""
813+ self.ui.user_email = 'test@me.com'
814+ self.ui.user_password = 'yadda-yedda'
815+ self.ui.on_verify_token_button_clicked()
816+ self.assertEqual(self.ui.backend._called['validate_email'],
817+ ((APP_NAME, self.ui.user_email,
818+ self.ui.user_password, EMAIL_TOKEN),
819+ dict(reply_handler=gui.NO_OP,
820+ error_handler=gui.NO_OP)))
821+
822 def test_on_verify_token_button_shows_processing_page(self):
823 """Verify token button triggers call to backend."""
824 self.click_verify_email_with_valid_data()
825@@ -1079,7 +1096,7 @@
826
827
828 class VerifyEmailValidationTestCase(UbuntuSSOClientTestCase):
829- """Test suite for the user registration (verify email page)."""
830+ """Test suite for the user registration validation (verify email page)."""
831
832 def setUp(self):
833 """Init."""
834@@ -1114,6 +1131,20 @@
835 self.assert_warnings_visibility()
836
837
838+class VerifyEmailLoginOnlyTestCase(VerifyEmailTestCase):
839+ """Test suite for the user login (verify email page)."""
840+
841+ kwargs = dict(app_name=APP_NAME, tc_uri=TC_URI, help_text=HELP_TEXT,
842+ login_only=True)
843+
844+
845+class VerifyEmailValidationLoginOnlyTestCase(VerifyEmailValidationTestCase):
846+ """Test suite for the user login validation (verify email page)."""
847+
848+ kwargs = dict(app_name=APP_NAME, tc_uri=TC_URI, help_text=HELP_TEXT,
849+ login_only=True)
850+
851+
852 class RegistrationValidationTestCase(UbuntuSSOClientTestCase):
853 """Test suite for the user registration validations."""
854
855@@ -1130,12 +1161,7 @@
856
857 self.assert_correct_entry_warning(self.ui.name_entry,
858 self.ui.FIELD_REQUIRED)
859- self.assertNotIn('register_user', self.ui.backend._called)
860-
861- # Unused variable 'skip'
862- # pylint: disable=W0612
863- test_warning_is_shown_if_name_empty.skip = \
864- 'Unused for now, will be hidden to save space (LP: #627440).'
865+ self.assertNotIn('register_with_name', self.ui.backend._called)
866
867 def test_warning_is_shown_if_empty_email(self):
868 """A warning message is shown if emails are empty."""
869@@ -1148,7 +1174,7 @@
870 self.ui.FIELD_REQUIRED)
871 self.assert_correct_entry_warning(self.ui.email2_entry,
872 self.ui.FIELD_REQUIRED)
873- self.assertNotIn('register_user', self.ui.backend._called)
874+ self.assertNotIn('register_with_name', self.ui.backend._called)
875
876 def test_warning_is_shown_if_email_mismatch(self):
877 """A warning message is shown if emails doesn't match."""
878@@ -1161,7 +1187,7 @@
879 self.ui.EMAIL_MISMATCH)
880 self.assert_correct_entry_warning(self.ui.email2_entry,
881 self.ui.EMAIL_MISMATCH)
882- self.assertNotIn('register_user', self.ui.backend._called)
883+ self.assertNotIn('register_with_name', self.ui.backend._called)
884
885 def test_warning_is_shown_if_invalid_email(self):
886 """A warning message is shown if email is invalid."""
887@@ -1174,7 +1200,7 @@
888 self.ui.EMAIL_INVALID)
889 self.assert_correct_entry_warning(self.ui.email2_entry,
890 self.ui.EMAIL_INVALID)
891- self.assertNotIn('register_user', self.ui.backend._called)
892+ self.assertNotIn('register_with_name', self.ui.backend._called)
893
894 def test_password_help_is_always_shown(self):
895 """Password help text is correctly displayed."""
896@@ -1182,7 +1208,7 @@
897 'password help text is visible.')
898 self.assertEqual(self.ui.password_help_label.get_text(),
899 self.ui.PASSWORD_HELP)
900- self.assertNotIn('register_user', self.ui.backend._called)
901+ self.assertNotIn('register_with_name', self.ui.backend._called)
902
903 def test_warning_is_shown_if_password_mismatch(self):
904 """A warning message is shown if password doesn't match."""
905@@ -1195,7 +1221,7 @@
906 self.ui.PASSWORD_MISMATCH)
907 self.assert_correct_entry_warning(self.ui.password2_entry,
908 self.ui.PASSWORD_MISMATCH)
909- self.assertNotIn('register_user', self.ui.backend._called)
910+ self.assertNotIn('register_with_name', self.ui.backend._called)
911
912 def test_warning_is_shown_if_password_too_weak(self):
913 """A warning message is shown if password is too weak."""
914@@ -1210,7 +1236,7 @@
915 self.ui.PASSWORD_TOO_WEAK)
916 self.assert_correct_entry_warning(self.ui.password2_entry,
917 self.ui.PASSWORD_TOO_WEAK)
918- self.assertNotIn('register_user', self.ui.backend._called)
919+ self.assertNotIn('register_with_name', self.ui.backend._called)
920
921 def test_warning_is_shown_if_tc_not_accepted(self):
922 """A warning message is shown if TC are not accepted."""
923@@ -1221,7 +1247,7 @@
924
925 self.assert_correct_label_warning(self.ui.tc_warning_label,
926 self.ui.TC_NOT_ACCEPTED)
927- self.assertNotIn('register_user', self.ui.backend._called)
928+ self.assertNotIn('register_with_name', self.ui.backend._called)
929
930 def test_warning_is_shown_if_not_captcha_solution(self):
931 """A warning message is shown if TC are not accepted."""
932@@ -1232,7 +1258,7 @@
933
934 self.assert_correct_entry_warning(self.ui.captcha_solution_entry,
935 self.ui.FIELD_REQUIRED)
936- self.assertNotIn('register_user', self.ui.backend._called)
937+ self.assertNotIn('register_with_name', self.ui.backend._called)
938
939 def test_no_warning_messages_if_valid_data(self):
940 """No warning messages are shown if the data is valid."""
941@@ -1328,6 +1354,12 @@
942 self.ui.on_login_error(app_name=APP_NAME, error=self.error)
943 self.assert_pages_visibility(login=True)
944
945+ def test_on_user_not_validated_morphs_to_verify_page(self):
946+ """On user not validated, the verify page is shown."""
947+ self.click_connect_with_valid_data()
948+ self.ui.on_user_not_validated(app_name=APP_NAME, email=EMAIL)
949+ self.assert_pages_visibility(verify_email=True)
950+
951 def test_on_login_error_a_warning_is_shown(self):
952 """On user login error, a warning is shown with proper wording."""
953 self.click_connect_with_valid_data()
954@@ -1377,6 +1409,12 @@
955 self.ui.login_ok_button.clicked()
956 self.assertTrue(self._called)
957
958+ def test_user_and_pass_are_cached(self):
959+ """Username and password are temporarly cached for further use."""
960+ self.click_connect_with_valid_data()
961+ self.assertEqual(self.ui.user_email, EMAIL)
962+ self.assertEqual(self.ui.user_password, PASSWORD)
963+
964
965 class LoginValidationTestCase(UbuntuSSOClientTestCase):
966 """Test suite for the user login validation."""
967@@ -1782,7 +1820,7 @@
968 """All the backend signals are listed to be binded."""
969 for sig in ('CaptchaGenerated', 'CaptchaGenerationError',
970 'UserRegistered', 'UserRegistrationError',
971- 'LoggedIn', 'LoginError',
972+ 'LoggedIn', 'LoginError', 'UserNotValidated',
973 'EmailValidated', 'EmailValidationError',
974 'PasswordResetTokenSent', 'PasswordResetError',
975 'PasswordChanged', 'PasswordChangeError'):
976@@ -1864,6 +1902,13 @@
977 self.ui._signals['LoginError'](mismatch_app_name, 'dummy')
978 self.assertFalse(self._called)
979
980+ def test_on_user_not_validated_is_not_called(self):
981+ """on_user_not_validated is not called if incorrect app_name."""
982+ self.patch(self.ui, 'on_user_not_validated', self._set_called)
983+ mismatch_app_name = self.ui.app_name * 2
984+ self.ui._signals['UserNotValidated'](mismatch_app_name, 'dummy')
985+ self.assertFalse(self._called)
986+
987 def test_on_password_reset_token_sent_is_not_called(self):
988 """on_password_reset_token_sent is not called if incorrect app_name."""
989 self.patch(self.ui, 'on_password_reset_token_sent', self._set_called)
990
991=== modified file 'ubuntu_sso/tests/test_main.py'
992--- ubuntu_sso/tests/test_main.py 2010-09-08 19:25:02 +0000
993+++ ubuntu_sso/tests/test_main.py 2011-04-12 19:01:16 +0000
994@@ -1,3 +1,5 @@
995+# -*- coding: utf-8 -*-
996+#
997 # test_main - tests for ubuntu_sso.main
998 #
999 # Author: Natalia Bidart <natalia.bidart@canonical.com>
1000@@ -63,6 +65,7 @@
1001 EMAIL_ALREADY_REGISTERED = 'a@example.com'
1002 EMAIL_TOKEN = 'B2Pgtf'
1003 HELP = 'help text'
1004+NAME = 'Juanito PĂ©rez'
1005 PASSWORD = 'be4tiFul'
1006 RESET_PASSWORD_TOKEN = '8G5Wtq'
1007 TOKEN = {u'consumer_key': u'xQ7xDAz',
1008@@ -107,7 +110,8 @@
1009 class FakedRegistrations(object):
1010 """Fake the registrations service."""
1011
1012- def register(self, email, password, captcha_id, captcha_solution):
1013+ def register(self, email, password, displayname,
1014+ captcha_id, captcha_solution):
1015 """Fake registration. Return a fix result."""
1016 if email == EMAIL_ALREADY_REGISTERED:
1017 return {'status': 'error',
1018@@ -152,6 +156,9 @@
1019 class FakedAccounts(object):
1020 """Fake the accounts service."""
1021
1022+ def __init__(self):
1023+ self.preferred_email = EMAIL
1024+
1025 def validate_email(self, email_token):
1026 """Fake the email validation. Return a fix result."""
1027 if email_token is None:
1028@@ -164,6 +171,17 @@
1029 else:
1030 return STATUS_EMAIL_OK
1031
1032+ # pylint: disable=E0202, C0103
1033+
1034+ def me(self):
1035+ """Fake the 'me' information."""
1036+ return {u'username': u'Wh46bKY',
1037+ u'preferred_email': self.preferred_email,
1038+ u'displayname': u'',
1039+ u'unverified_emails': [u'aaaaaa@example.com'],
1040+ u'verified_emails': [],
1041+ u'openid_identifier': u'Wh46bKY'}
1042+
1043
1044 class FakedSSOServer(object):
1045 """Fake an SSO server."""
1046@@ -279,6 +297,29 @@
1047 result = self.processor.login(**self.login_kwargs)
1048 self.assertEqual(TOKEN, result, 'authentication was successful.')
1049
1050+ # is_validated
1051+
1052+ def test_is_validated(self):
1053+ """If preferred email is not None, user is validated."""
1054+ result = self.processor.is_validated(token=TOKEN)
1055+ self.assertTrue(result, 'user must be validated.')
1056+
1057+ def test_is_not_validated(self):
1058+ """If preferred email is None, user is not validated."""
1059+ service = FakedSSOServer(None, None)
1060+ service.accounts.preferred_email = None
1061+ result = self.processor.is_validated(sso_service=service,
1062+ token=TOKEN)
1063+ self.assertFalse(result, 'user must not be validated.')
1064+
1065+ def test_is_not_validated_empty_result(self):
1066+ """If preferred email is None, user is not validated."""
1067+ service = FakedSSOServer(None, None)
1068+ service.accounts.me = lambda: {}
1069+ result = self.processor.is_validated(sso_service=service,
1070+ token=TOKEN)
1071+ self.assertFalse(result, 'user must not be validated.')
1072+
1073 # validate_email
1074
1075 def test_validate_email_if_status_ok(self):
1076@@ -362,6 +403,16 @@
1077 self.assertIn('Received invalid reply: %s' % STATUS_UNKNOWN, exc)
1078
1079
1080+class SSORegistrationWithNameTestCase(SSOLoginProcessorTestCase):
1081+ """Test suite for the SSO login processor for register_with_name."""
1082+
1083+ def setUp(self):
1084+ """Init."""
1085+ super(SSORegistrationWithNameTestCase, self).setUp()
1086+ self.register_kwargs['displayname'] = NAME
1087+ self.processor.register_user = self.processor.register_with_name
1088+
1089+
1090 class BlockingSampleException(Exception):
1091 """The exception that will be thrown by the fake blocking."""
1092
1093@@ -380,12 +431,13 @@
1094 mockbus._register_object_path(ARGS)
1095 self.mockprocessorclass = None
1096
1097- def ksc(k, val):
1098+ def ksc(k, val, callback, *cb_args):
1099 """Assert over token and app_name."""
1100 self.assertEqual(k, APP_NAME)
1101 self.assertEqual(val, TOKEN)
1102 self.keyring_was_set = True
1103 self.keyring_values = k, val
1104+ callback(*cb_args)
1105
1106 self.patch(ubuntu_sso.main, "keyring_store_credentials", ksc)
1107 self.keyring_was_set = False
1108@@ -472,8 +524,8 @@
1109 """Test that the register_user method works ok."""
1110 d = Deferred()
1111 expected_result = "expected result"
1112- self.create_mock_processor().register_user(EMAIL, PASSWORD, CAPTCHA_ID,
1113- CAPTCHA_SOLUTION)
1114+ self.create_mock_processor().register_user(EMAIL, PASSWORD,
1115+ CAPTCHA_ID, CAPTCHA_SOLUTION)
1116 self.mocker.result(expected_result)
1117 self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking)
1118 self.mocker.replay()
1119@@ -496,8 +548,8 @@
1120 """Test that the register_user method fails as expected."""
1121 d = Deferred()
1122 expected_result = "expected result"
1123- self.create_mock_processor().register_user(EMAIL, PASSWORD, CAPTCHA_ID,
1124- CAPTCHA_SOLUTION)
1125+ self.create_mock_processor().register_user(EMAIL, PASSWORD,
1126+ CAPTCHA_ID, CAPTCHA_SOLUTION)
1127 self.mocker.result(expected_result)
1128 self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking)
1129 self.mocker.replay()
1130@@ -516,11 +568,62 @@
1131 CAPTCHA_SOLUTION)
1132 return d
1133
1134+ def test_register_with_name(self):
1135+ """Test that the register_with_name method works ok."""
1136+ d = Deferred()
1137+ expected_result = "expected result"
1138+ self.create_mock_processor().register_with_name(EMAIL, PASSWORD, NAME,
1139+ CAPTCHA_ID, CAPTCHA_SOLUTION)
1140+ self.mocker.result(expected_result)
1141+ self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking)
1142+ self.mocker.replay()
1143+
1144+ def verify(app_name, result):
1145+ """The actual test."""
1146+ self.assertEqual(result, expected_result)
1147+ self.assertEqual(app_name, APP_NAME)
1148+ d.callback(result)
1149+
1150+ client = SSOLogin(self.mockbusname,
1151+ sso_login_processor_class=self.mockprocessorclass)
1152+ self.patch(client, "UserRegistered", verify)
1153+ self.patch(client, "UserRegistrationError", d.errback)
1154+ client.register_with_name(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID,
1155+ CAPTCHA_SOLUTION)
1156+ return d
1157+
1158+ def test_register_with_name_error(self):
1159+ """Test that the register_with_name method fails as expected."""
1160+ d = Deferred()
1161+ expected_result = "expected result"
1162+ self.create_mock_processor().register_with_name(EMAIL, PASSWORD, NAME,
1163+ CAPTCHA_ID, CAPTCHA_SOLUTION)
1164+ self.mocker.result(expected_result)
1165+ self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking)
1166+ self.mocker.replay()
1167+
1168+ def verify(app_name, errdict):
1169+ """The actual test."""
1170+ self.assertEqual(errdict["errtype"], "BlockingSampleException")
1171+ self.assertEqual(app_name, APP_NAME)
1172+ d.callback("Ok")
1173+
1174+ client = SSOLogin(self.mockbusname,
1175+ sso_login_processor_class=self.mockprocessorclass)
1176+ self.patch(client, "UserRegistered", d.errback)
1177+ self.patch(client, "UserRegistrationError", verify)
1178+ client.register_with_name(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID,
1179+ CAPTCHA_SOLUTION)
1180+ return d
1181+
1182 def test_login(self):
1183 """Test that the login method works ok."""
1184 d = Deferred()
1185- self.create_mock_processor().login(EMAIL, PASSWORD, TOKEN_NAME)
1186+ processor = self.create_mock_processor()
1187+ processor.login(EMAIL, PASSWORD, TOKEN_NAME)
1188 self.mocker.result(TOKEN)
1189+ processor.is_validated(TOKEN)
1190+ self.mocker.result(True)
1191 self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking)
1192 self.mocker.replay()
1193
1194@@ -535,13 +638,40 @@
1195 sso_login_processor_class=self.mockprocessorclass)
1196 self.patch(client, "LoggedIn", verify)
1197 self.patch(client, "LoginError", d.errback)
1198+ self.patch(client, "UserNotValidated", d.errback)
1199+ client.login(APP_NAME, EMAIL, PASSWORD)
1200+ return d
1201+
1202+ def test_login_user_not_validated(self):
1203+ """Test that the login sends EmailNotValidated signal."""
1204+ d = Deferred()
1205+ processor = self.create_mock_processor()
1206+ processor.login(EMAIL, PASSWORD, TOKEN_NAME)
1207+ self.mocker.result(TOKEN)
1208+ processor.is_validated(TOKEN)
1209+ self.mocker.result(False)
1210+ self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking)
1211+ self.mocker.replay()
1212+
1213+ def verify(app_name, email):
1214+ """The actual test."""
1215+ self.assertEqual(app_name, APP_NAME)
1216+ self.assertEqual(email, EMAIL)
1217+ self.assertFalse(self.keyring_was_set, "Keyring should not be set")
1218+ d.callback("Ok")
1219+
1220+ client = SSOLogin(self.mockbusname,
1221+ sso_login_processor_class=self.mockprocessorclass)
1222+ self.patch(client, "LoggedIn", d.errback)
1223+ self.patch(client, "LoginError", d.errback)
1224+ self.patch(client, "UserNotValidated", verify)
1225 client.login(APP_NAME, EMAIL, PASSWORD)
1226 return d
1227
1228 def test_login_error(self):
1229 """Test that the login method fails as expected."""
1230 d = Deferred()
1231- self.mockprocessorclass = self.mocker.mock()
1232+ self.create_mock_processor()
1233 self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking)
1234
1235 def fake_gtn(*args):
1236@@ -562,6 +692,7 @@
1237 sso_login_processor_class=self.mockprocessorclass)
1238 self.patch(client, "LoggedIn", d.errback)
1239 self.patch(client, "LoginError", verify)
1240+ self.patch(client, "UserNotValidated", d.errback)
1241 client.login(APP_NAME, EMAIL, PASSWORD)
1242 return d
1243
1244@@ -591,7 +722,7 @@
1245 def test_validate_email_error(self):
1246 """Test that the validate_email method fails as expected."""
1247 d = Deferred()
1248- self.mockprocessorclass = self.mocker.mock()
1249+ self.create_mock_processor()
1250 self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking)
1251
1252 def fake_gtn(*args):
1253@@ -802,7 +933,7 @@
1254 self.assertEqual(result["errtype"], e.__class__.__name__)
1255
1256
1257-class KeyringCredentialsTestCase(MockerTestCase):
1258+class KeyringCredentialsTestCase(TestCase, MockerTestCase):
1259 """Check the functions that access the keyring."""
1260
1261 # Invalid name (should match ([a-z_][a-z0-9_]*|[A-Z_][A-Z0-9_]*)$)
1262@@ -810,15 +941,19 @@
1263
1264 def test_keyring_store_cred(self):
1265 """Verify the method that stores credentials."""
1266+ idle_add = lambda f, *args, **kwargs: f(*args, **kwargs)
1267+ self.patch(gobject, "idle_add", idle_add)
1268 token_value = TOKEN
1269 mockKeyringClass = self.mocker.replace("ubuntu_sso.keyring.Keyring")
1270 mockKeyringClass(APP_NAME)
1271 mockKeyring = self.mocker.mock()
1272+ callback = self.mocker.mock()
1273 self.mocker.result(mockKeyring)
1274 mockKeyring.set_ubuntusso_attr(token_value)
1275+ callback(1, 2, 3)
1276 self.mocker.replay()
1277
1278- keyring_store_credentials(APP_NAME, token_value)
1279+ keyring_store_credentials(APP_NAME, token_value, callback, 1, 2, 3)
1280
1281 def test_keyring_get_cred(self):
1282 """The method returns the right token."""
1283@@ -1367,6 +1502,24 @@
1284 self.assertEqual(self.calls[0][0], 'CredentialsError')
1285 self.assertEqual(self.calls[0][1][0], APP_NAME)
1286
1287+ def test_credentials_are_not_stored_if_ping_failed(self):
1288+ """Credentials are not stored if the ping fails."""
1289+
1290+ def fail(*args, **kwargs):
1291+ """Raise an exception."""
1292+ self.args = AssertionError((args, kwargs))
1293+ # pylint: disable=E0702
1294+ raise self.args
1295+
1296+ self.patch(self.client, '_ping_url', fail)
1297+ self._patch('clear_token')
1298+
1299+ self.client._login_success_cb(None, APP_NAME, EMAIL)
1300+
1301+ self.assertEqual(len(self.calls), 1)
1302+ self.assertEqual(self.calls[0][0], 'clear_token')
1303+ self.assertEqual(self.calls[0][1][0], APP_NAME)
1304+
1305
1306 class EnvironOverridesTestCase(TestCase):
1307 """Some URLs can be set from the environment for testing/QA purposes."""

Subscribers

People subscribed via source and target branches

to all changes: