Merge lp:~nataliabidart/ubuntu/maverick/ubuntu-sso-client/ubuntu-sso-client-1.0.9 into lp:ubuntu/maverick/ubuntu-sso-client
- Maverick (10.10)
- ubuntu-sso-client-1.0.9
- Merge into maverick
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Sponsors | Pending | ||
Review via email: mp+57379@code.launchpad.net |
Commit message
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).
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
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.""" |