Merge lp:~nataliabidart/ubuntu-sso-client/handle-user-not-validated-stable into lp:ubuntu-sso-client/stable-1-0
- handle-user-not-validated-stable
- Merge into stable-1-0
Proposed by
Natalia Bidart
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Natalia Bidart | ||||
Approved revision: | 637 | ||||
Merged at revision: | 637 | ||||
Proposed branch: | lp:~nataliabidart/ubuntu-sso-client/handle-user-not-validated-stable | ||||
Merge into: | lp:ubuntu-sso-client/stable-1-0 | ||||
Diff against target: |
449 lines (+173/-34) 4 files modified
ubuntu_sso/gui.py (+19/-6) ubuntu_sso/main.py (+57/-24) ubuntu_sso/tests/test_gui.py (+26/-1) ubuntu_sso/tests/test_main.py (+71/-3) |
||||
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu-sso-client/handle-user-not-validated-stable | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alejandro J. Cura (community) | Approve | ||
Roman Yepishev (community) | fieldtest | Approve | |
Review via email: mp+39786@code.launchpad.net |
Commit message
* Added a new DBus signal UserNotValidated to indicate when a user is registered but not validated (LP: #667899).
* Added new workflow so email validation is requested if necessary.
Description of the change
To test, follow the same procedure as in
https:/
but use instead the ApplicationCred
'Ubuntu One', '', '', 0
The rest of the workflow is the same.
To post a comment you must log in.
Revision history for this message
Alejandro J. Cura (alecu) wrote : | # |
I approve of this branch.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'ubuntu_sso/gui.py' | |||
2 | --- ubuntu_sso/gui.py 2010-10-01 13:14:34 +0000 | |||
3 | +++ ubuntu_sso/gui.py 2010-11-01 21:13:47 +0000 | |||
4 | @@ -252,6 +252,8 @@ | |||
5 | 252 | self.tc_uri = tc_uri | 252 | self.tc_uri = tc_uri |
6 | 253 | self.help_text = help_text | 253 | self.help_text = help_text |
7 | 254 | self.close_callback = close_callback | 254 | self.close_callback = close_callback |
8 | 255 | self.user_email = None | ||
9 | 256 | self.user_password = None | ||
10 | 255 | 257 | ||
11 | 256 | ui_filename = get_data_file('ui.glade') | 258 | ui_filename = get_data_file('ui.glade') |
12 | 257 | builder = gtk.Builder() | 259 | builder = gtk.Builder() |
13 | @@ -357,6 +359,8 @@ | |||
14 | 357 | self._filter_by_app_name(self.on_logged_in), | 359 | self._filter_by_app_name(self.on_logged_in), |
15 | 358 | 'LoginError': | 360 | 'LoginError': |
16 | 359 | self._filter_by_app_name(self.on_login_error), | 361 | self._filter_by_app_name(self.on_login_error), |
17 | 362 | 'UserNotValidated': | ||
18 | 363 | self._filter_by_app_name(self.on_user_not_validated), | ||
19 | 360 | 'PasswordResetTokenSent': | 364 | 'PasswordResetTokenSent': |
20 | 361 | self._filter_by_app_name(self.on_password_reset_token_sent), | 365 | self._filter_by_app_name(self.on_password_reset_token_sent), |
21 | 362 | 'PasswordResetError': | 366 | 'PasswordResetError': |
22 | @@ -417,8 +421,8 @@ | |||
23 | 417 | 421 | ||
24 | 418 | match = self.bus.add_signal_receiver(method, signal_name=signal, | 422 | match = self.bus.add_signal_receiver(method, signal_name=signal, |
25 | 419 | dbus_interface=iface) | 423 | dbus_interface=iface) |
28 | 420 | logger.info('Connecting signal %r with method %r at iface %r.' \ | 424 | logger.debug('Connecting signal %r with method %r at iface %r.' \ |
29 | 421 | 'Match: %r', signal, method, iface, match) | 425 | 'Match: %r', signal, method, iface, match) |
30 | 422 | self._signals_receivers[(iface, signal)] = method | 426 | self._signals_receivers[(iface, signal)] = method |
31 | 423 | 427 | ||
32 | 424 | def _debug(self, *args, **kwargs): | 428 | def _debug(self, *args, **kwargs): |
33 | @@ -725,8 +729,8 @@ | |||
34 | 725 | remove = self.bus.remove_signal_receiver | 729 | remove = self.bus.remove_signal_receiver |
35 | 726 | for (iface, signal) in self._signals_receivers.keys(): | 730 | for (iface, signal) in self._signals_receivers.keys(): |
36 | 727 | method = self._signals_receivers.pop((iface, signal)) | 731 | method = self._signals_receivers.pop((iface, signal)) |
39 | 728 | logger.info('Removing signal %r with method %r at iface %r.', | 732 | logger.debug('Removing signal %r with method %r at iface %r.', |
40 | 729 | signal, method, iface) | 733 | signal, method, iface) |
41 | 730 | remove(method, signal_name=signal, dbus_interface=iface) | 734 | remove(method, signal_name=signal, dbus_interface=iface) |
42 | 731 | 735 | ||
43 | 732 | # hide the main window | 736 | # hide the main window |
44 | @@ -804,6 +808,8 @@ | |||
45 | 804 | return | 808 | return |
46 | 805 | 809 | ||
47 | 806 | self._set_current_page(self.processing_vbox) | 810 | self._set_current_page(self.processing_vbox) |
48 | 811 | self.user_email = email1 | ||
49 | 812 | self.user_password = password1 | ||
50 | 807 | 813 | ||
51 | 808 | logger.info('Calling register_user with email %r, password <hidden>,' \ | 814 | logger.info('Calling register_user with email %r, password <hidden>,' \ |
52 | 809 | ' captcha_id %r and captcha_solution %r.', email1, | 815 | ' captcha_id %r and captcha_solution %r.', email1, |
53 | @@ -832,8 +838,8 @@ | |||
54 | 832 | self.email_token_entry.set_warning(self.FIELD_REQUIRED) | 838 | self.email_token_entry.set_warning(self.FIELD_REQUIRED) |
55 | 833 | return | 839 | return |
56 | 834 | 840 | ||
59 | 835 | email = self.email1_entry.get_text() | 841 | email = self.user_email |
60 | 836 | password = self.password1_entry.get_text() | 842 | password = self.user_password |
61 | 837 | f = self.backend.validate_email | 843 | f = self.backend.validate_email |
62 | 838 | logger.info('Calling validate_email with email %r, password <hidden>' \ | 844 | logger.info('Calling validate_email with email %r, password <hidden>' \ |
63 | 839 | ', app_name %r and email_token %r.', email, self.app_name, | 845 | ', app_name %r and email_token %r.', email, self.app_name, |
64 | @@ -871,6 +877,8 @@ | |||
65 | 871 | reply_handler=NO_OP, error_handler=NO_OP) | 877 | reply_handler=NO_OP, error_handler=NO_OP) |
66 | 872 | 878 | ||
67 | 873 | self._set_current_page(self.processing_vbox) | 879 | self._set_current_page(self.processing_vbox) |
68 | 880 | self.user_email = email | ||
69 | 881 | self.user_password = password | ||
70 | 874 | 882 | ||
71 | 875 | def on_login_back_button_clicked(self, *args, **kwargs): | 883 | def on_login_back_button_clicked(self, *args, **kwargs): |
72 | 876 | """User wants to go to the previous page.""" | 884 | """User wants to go to the previous page.""" |
73 | @@ -1061,6 +1069,11 @@ | |||
74 | 1061 | msg)) | 1069 | msg)) |
75 | 1062 | 1070 | ||
76 | 1063 | @log_call | 1071 | @log_call |
77 | 1072 | def on_user_not_validated(self, app_name, email, *args, **kwargs): | ||
78 | 1073 | """User was not validated.""" | ||
79 | 1074 | self.on_user_registered(app_name, email) | ||
80 | 1075 | |||
81 | 1076 | @log_call | ||
82 | 1064 | def on_password_reset_token_sent(self, app_name, email, *args, **kwargs): | 1077 | def on_password_reset_token_sent(self, app_name, email, *args, **kwargs): |
83 | 1065 | """Password reset token was successfully sent.""" | 1078 | """Password reset token was successfully sent.""" |
84 | 1066 | msg = self.SET_NEW_PASSWORD_LABEL % {'email': email} | 1079 | msg = self.SET_NEW_PASSWORD_LABEL % {'email': email} |
85 | 1067 | 1080 | ||
86 | === modified file 'ubuntu_sso/main.py' | |||
87 | --- ubuntu_sso/main.py 2010-10-18 17:34:04 +0000 | |||
88 | +++ ubuntu_sso/main.py 2010-11-01 21:13:47 +0000 | |||
89 | @@ -209,6 +209,27 @@ | |||
90 | 209 | 'token_name: %r', credentials['consumer_key'], token_name) | 209 | 'token_name: %r', credentials['consumer_key'], token_name) |
91 | 210 | return credentials | 210 | return credentials |
92 | 211 | 211 | ||
93 | 212 | def is_validated(self, email, password, token_name, sso_service=None): | ||
94 | 213 | """Return if user with 'email' and 'password' is validated.""" | ||
95 | 214 | if sso_service is None: | ||
96 | 215 | token = self.login(email=email, password=password, | ||
97 | 216 | token_name=token_name) | ||
98 | 217 | |||
99 | 218 | oauth_token = oauth.OAuthToken(token['token'], | ||
100 | 219 | token['token_secret']) | ||
101 | 220 | authorizer = OAuthAuthorizer(token['consumer_key'], | ||
102 | 221 | token['consumer_secret'], | ||
103 | 222 | oauth_token) | ||
104 | 223 | sso_service = self.sso_service_class(authorizer, self.service_url) | ||
105 | 224 | |||
106 | 225 | me_info = sso_service.accounts.me() | ||
107 | 226 | key = 'preferred_email' | ||
108 | 227 | result = key in me_info and me_info[key] != None | ||
109 | 228 | |||
110 | 229 | logger.debug('is_validated: email: %r token_name: %r, result: %r.', | ||
111 | 230 | email, token_name, result) | ||
112 | 231 | return result | ||
113 | 232 | |||
114 | 212 | def validate_email(self, email, password, email_token, token_name): | 233 | def validate_email(self, email, password, email_token, token_name): |
115 | 213 | """Validate an email token for user with 'email' and 'password'.""" | 234 | """Validate an email token for user with 'email' and 'password'.""" |
116 | 214 | logger.debug('validate_email: email: %r password: <hidden>, ' | 235 | logger.debug('validate_email: email: %r password: <hidden>, ' |
117 | @@ -311,12 +332,8 @@ | |||
118 | 311 | dbus.service.Object.__init__(self, object_path="/sso", | 332 | dbus.service.Object.__init__(self, object_path="/sso", |
119 | 312 | bus_name=bus_name) | 333 | bus_name=bus_name) |
120 | 313 | self.sso_login_processor_class = sso_login_processor_class | 334 | self.sso_login_processor_class = sso_login_processor_class |
127 | 314 | self.sso_service_class = sso_service_class | 335 | self.processor = self.sso_login_processor_class( |
128 | 315 | 336 | sso_service_class=sso_service_class) | |
123 | 316 | def processor(self): | ||
124 | 317 | """Create a login processor with the given class and service class.""" | ||
125 | 318 | return self.sso_login_processor_class( | ||
126 | 319 | sso_service_class=self.sso_service_class) | ||
129 | 320 | 337 | ||
130 | 321 | # generate_capcha signals | 338 | # generate_capcha signals |
131 | 322 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 339 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
132 | @@ -337,7 +354,7 @@ | |||
133 | 337 | """Call the matching method in the processor.""" | 354 | """Call the matching method in the processor.""" |
134 | 338 | def f(): | 355 | def f(): |
135 | 339 | """Inner function that will be run in a thread.""" | 356 | """Inner function that will be run in a thread.""" |
137 | 340 | return self.processor().generate_captcha(filename) | 357 | return self.processor.generate_captcha(filename) |
138 | 341 | blocking(f, app_name, self.CaptchaGenerated, | 358 | blocking(f, app_name, self.CaptchaGenerated, |
139 | 342 | self.CaptchaGenerationError) | 359 | self.CaptchaGenerationError) |
140 | 343 | 360 | ||
141 | @@ -361,8 +378,8 @@ | |||
142 | 361 | """Call the matching method in the processor.""" | 378 | """Call the matching method in the processor.""" |
143 | 362 | def f(): | 379 | def f(): |
144 | 363 | """Inner function that will be run in a thread.""" | 380 | """Inner function that will be run in a thread.""" |
147 | 364 | return self.processor().register_user(email, password, | 381 | return self.processor.register_user(email, password, |
148 | 365 | captcha_id, captcha_solution) | 382 | captcha_id, captcha_solution) |
149 | 366 | blocking(f, app_name, self.UserRegistered, self.UserRegistrationError) | 383 | blocking(f, app_name, self.UserRegistered, self.UserRegistrationError) |
150 | 367 | 384 | ||
151 | 368 | # login signals | 385 | # login signals |
152 | @@ -378,6 +395,12 @@ | |||
153 | 378 | logger.debug('SSOLogin: emitting LoginError with ' | 395 | logger.debug('SSOLogin: emitting LoginError with ' |
154 | 379 | 'app_name "%s" and error %r', app_name, error) | 396 | 'app_name "%s" and error %r', app_name, error) |
155 | 380 | 397 | ||
156 | 398 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | ||
157 | 399 | def UserNotValidated(self, app_name, result): | ||
158 | 400 | """Signal thrown when the user is not validated.""" | ||
159 | 401 | logger.debug('SSOLogin: emitting UserNotValidated with app_name "%s" ' | ||
160 | 402 | 'and result %r', app_name, result) | ||
161 | 403 | |||
162 | 381 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | 404 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, |
163 | 382 | in_signature='sss') | 405 | in_signature='sss') |
164 | 383 | def login(self, app_name, email, password): | 406 | def login(self, app_name, email, password): |
165 | @@ -387,13 +410,23 @@ | |||
166 | 387 | token_name = get_token_name(app_name) | 410 | token_name = get_token_name(app_name) |
167 | 388 | logger.debug('login: token_name %r, email %r, password <hidden>.', | 411 | logger.debug('login: token_name %r, email %r, password <hidden>.', |
168 | 389 | token_name, email) | 412 | token_name, email) |
170 | 390 | credentials = self.processor().login(email, password, token_name) | 413 | credentials = self.processor.login(email, password, token_name) |
171 | 391 | logger.debug('login returned not None credentials? %r.', | 414 | logger.debug('login returned not None credentials? %r.', |
172 | 392 | credentials is not None) | 415 | credentials is not None) |
177 | 393 | assert credentials is not None | 416 | return credentials |
178 | 394 | keyring_store_credentials(app_name, credentials) | 417 | |
179 | 395 | return email | 418 | def success_cb(app_name, credentials): |
180 | 396 | blocking(f, app_name, self.LoggedIn, self.LoginError) | 419 | """Login finished successfull.""" |
181 | 420 | token_name = get_token_name(app_name) | ||
182 | 421 | is_validated = self.processor.is_validated(email, password, | ||
183 | 422 | token_name) | ||
184 | 423 | logger.debug('user is validated? %r.', is_validated) | ||
185 | 424 | if is_validated: | ||
186 | 425 | keyring_store_credentials(app_name, credentials) | ||
187 | 426 | self.LoggedIn(app_name, email) | ||
188 | 427 | else: | ||
189 | 428 | self.UserNotValidated(app_name, email) | ||
190 | 429 | blocking(f, app_name, success_cb, self.LoginError) | ||
191 | 397 | 430 | ||
192 | 398 | # validate_email signals | 431 | # validate_email signals |
193 | 399 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 432 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
194 | @@ -415,8 +448,8 @@ | |||
195 | 415 | def f(): | 448 | def f(): |
196 | 416 | """Inner function that will be run in a thread.""" | 449 | """Inner function that will be run in a thread.""" |
197 | 417 | token_name = get_token_name(app_name) | 450 | token_name = get_token_name(app_name) |
200 | 418 | credentials = self.processor().validate_email(email, password, | 451 | credentials = self.processor.validate_email(email, password, |
201 | 419 | email_token, token_name) | 452 | email_token, token_name) |
202 | 420 | keyring_store_credentials(app_name, credentials) | 453 | keyring_store_credentials(app_name, credentials) |
203 | 421 | return email | 454 | return email |
204 | 422 | blocking(f, app_name, self.EmailValidated, self.EmailValidationError) | 455 | blocking(f, app_name, self.EmailValidated, self.EmailValidationError) |
205 | @@ -440,7 +473,7 @@ | |||
206 | 440 | """Call the matching method in the processor.""" | 473 | """Call the matching method in the processor.""" |
207 | 441 | def f(): | 474 | def f(): |
208 | 442 | """Inner function that will be run in a thread.""" | 475 | """Inner function that will be run in a thread.""" |
210 | 443 | return self.processor().request_password_reset_token(email) | 476 | return self.processor.request_password_reset_token(email) |
211 | 444 | blocking(f, app_name, self.PasswordResetTokenSent, | 477 | blocking(f, app_name, self.PasswordResetTokenSent, |
212 | 445 | self.PasswordResetError) | 478 | self.PasswordResetError) |
213 | 446 | 479 | ||
214 | @@ -463,8 +496,8 @@ | |||
215 | 463 | """Call the matching method in the processor.""" | 496 | """Call the matching method in the processor.""" |
216 | 464 | def f(): | 497 | def f(): |
217 | 465 | """Inner function that will be run in a thread.""" | 498 | """Inner function that will be run in a thread.""" |
220 | 466 | return self.processor().set_new_password(email, token, | 499 | return self.processor.set_new_password(email, token, |
221 | 467 | new_password) | 500 | new_password) |
222 | 468 | blocking(f, app_name, self.PasswordChanged, self.PasswordChangeError) | 501 | blocking(f, app_name, self.PasswordChanged, self.PasswordChangeError) |
223 | 469 | 502 | ||
224 | 470 | 503 | ||
225 | @@ -481,19 +514,19 @@ | |||
226 | 481 | @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="s") | 514 | @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="s") |
227 | 482 | def AuthorizationDenied(self, app_name): | 515 | def AuthorizationDenied(self, app_name): |
228 | 483 | """Signal thrown when the user denies the authorization.""" | 516 | """Signal thrown when the user denies the authorization.""" |
231 | 484 | logger.info('SSOLogin: emitting AuthorizationDenied with app_name ' | 517 | logger.info('SSOCredentials: emitting AuthorizationDenied with ' |
232 | 485 | '"%s"', app_name) | 518 | 'app_name "%s"', app_name) |
233 | 486 | 519 | ||
234 | 487 | @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sa{ss}") | 520 | @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sa{ss}") |
235 | 488 | def CredentialsFound(self, app_name, credentials): | 521 | def CredentialsFound(self, app_name, credentials): |
236 | 489 | """Signal thrown when the credentials are found.""" | 522 | """Signal thrown when the credentials are found.""" |
239 | 490 | logger.info('SSOLogin: emitting CredentialsFound with app_name "%s"', | 523 | logger.info('SSOCredentials: emitting CredentialsFound with ' |
240 | 491 | app_name) | 524 | 'app_name "%s"', app_name) |
241 | 492 | 525 | ||
242 | 493 | @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sss") | 526 | @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="sss") |
243 | 494 | def CredentialsError(self, app_name, error_message, detailed_error): | 527 | def CredentialsError(self, app_name, error_message, detailed_error): |
244 | 495 | """Signal thrown when there is a problem finding the credentials.""" | 528 | """Signal thrown when there is a problem finding the credentials.""" |
246 | 496 | logger.debug('SSOCredentials: emitting CredentialsError with app_name ' | 529 | logger.error('SSOCredentials: emitting CredentialsError with app_name ' |
247 | 497 | '"%s" and error_message %r', app_name, error_message) | 530 | '"%s" and error_message %r', app_name, error_message) |
248 | 498 | 531 | ||
249 | 499 | @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME, | 532 | @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME, |
250 | 500 | 533 | ||
251 | === modified file 'ubuntu_sso/tests/test_gui.py' | |||
252 | --- ubuntu_sso/tests/test_gui.py 2010-09-07 13:57:35 +0000 | |||
253 | +++ ubuntu_sso/tests/test_gui.py 2010-11-01 21:13:47 +0000 | |||
254 | @@ -856,6 +856,12 @@ | |||
255 | 856 | self.ui.join_ok_button.clicked() | 856 | self.ui.join_ok_button.clicked() |
256 | 857 | self.assertTrue(self._called) | 857 | self.assertTrue(self._called) |
257 | 858 | 858 | ||
258 | 859 | def test_user_and_pass_are_cached(self): | ||
259 | 860 | """Username and password are temporarly cached for further use.""" | ||
260 | 861 | self.click_join_with_valid_data() | ||
261 | 862 | self.assertEqual(self.ui.user_email, EMAIL) | ||
262 | 863 | self.assertEqual(self.ui.user_password, PASSWORD) | ||
263 | 864 | |||
264 | 859 | 865 | ||
265 | 860 | class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase): | 866 | class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase): |
266 | 861 | """Test suite for the user registration (with no t&c link).""" | 867 | """Test suite for the user registration (with no t&c link).""" |
267 | @@ -1328,6 +1334,12 @@ | |||
268 | 1328 | self.ui.on_login_error(app_name=APP_NAME, error=self.error) | 1334 | self.ui.on_login_error(app_name=APP_NAME, error=self.error) |
269 | 1329 | self.assert_pages_visibility(login=True) | 1335 | self.assert_pages_visibility(login=True) |
270 | 1330 | 1336 | ||
271 | 1337 | def test_on_user_not_validated_morphs_to_verify_page(self): | ||
272 | 1338 | """On user not validated, the verify page is shown.""" | ||
273 | 1339 | self.click_connect_with_valid_data() | ||
274 | 1340 | self.ui.on_user_not_validated(app_name=APP_NAME, email=EMAIL) | ||
275 | 1341 | self.assert_pages_visibility(verify_email=True) | ||
276 | 1342 | |||
277 | 1331 | def test_on_login_error_a_warning_is_shown(self): | 1343 | def test_on_login_error_a_warning_is_shown(self): |
278 | 1332 | """On user login error, a warning is shown with proper wording.""" | 1344 | """On user login error, a warning is shown with proper wording.""" |
279 | 1333 | self.click_connect_with_valid_data() | 1345 | self.click_connect_with_valid_data() |
280 | @@ -1377,6 +1389,12 @@ | |||
281 | 1377 | self.ui.login_ok_button.clicked() | 1389 | self.ui.login_ok_button.clicked() |
282 | 1378 | self.assertTrue(self._called) | 1390 | self.assertTrue(self._called) |
283 | 1379 | 1391 | ||
284 | 1392 | def test_user_and_pass_are_cached(self): | ||
285 | 1393 | """Username and password are temporarly cached for further use.""" | ||
286 | 1394 | self.click_connect_with_valid_data() | ||
287 | 1395 | self.assertEqual(self.ui.user_email, EMAIL) | ||
288 | 1396 | self.assertEqual(self.ui.user_password, PASSWORD) | ||
289 | 1397 | |||
290 | 1380 | 1398 | ||
291 | 1381 | class LoginValidationTestCase(UbuntuSSOClientTestCase): | 1399 | class LoginValidationTestCase(UbuntuSSOClientTestCase): |
292 | 1382 | """Test suite for the user login validation.""" | 1400 | """Test suite for the user login validation.""" |
293 | @@ -1782,7 +1800,7 @@ | |||
294 | 1782 | """All the backend signals are listed to be binded.""" | 1800 | """All the backend signals are listed to be binded.""" |
295 | 1783 | for sig in ('CaptchaGenerated', 'CaptchaGenerationError', | 1801 | for sig in ('CaptchaGenerated', 'CaptchaGenerationError', |
296 | 1784 | 'UserRegistered', 'UserRegistrationError', | 1802 | 'UserRegistered', 'UserRegistrationError', |
298 | 1785 | 'LoggedIn', 'LoginError', | 1803 | 'LoggedIn', 'LoginError', 'UserNotValidated', |
299 | 1786 | 'EmailValidated', 'EmailValidationError', | 1804 | 'EmailValidated', 'EmailValidationError', |
300 | 1787 | 'PasswordResetTokenSent', 'PasswordResetError', | 1805 | 'PasswordResetTokenSent', 'PasswordResetError', |
301 | 1788 | 'PasswordChanged', 'PasswordChangeError'): | 1806 | 'PasswordChanged', 'PasswordChangeError'): |
302 | @@ -1864,6 +1882,13 @@ | |||
303 | 1864 | self.ui._signals['LoginError'](mismatch_app_name, 'dummy') | 1882 | self.ui._signals['LoginError'](mismatch_app_name, 'dummy') |
304 | 1865 | self.assertFalse(self._called) | 1883 | self.assertFalse(self._called) |
305 | 1866 | 1884 | ||
306 | 1885 | def test_on_user_not_validated_is_not_called(self): | ||
307 | 1886 | """on_user_not_validated is not called if incorrect app_name.""" | ||
308 | 1887 | self.patch(self.ui, 'on_user_not_validated', self._set_called) | ||
309 | 1888 | mismatch_app_name = self.ui.app_name * 2 | ||
310 | 1889 | self.ui._signals['UserNotValidated'](mismatch_app_name, 'dummy') | ||
311 | 1890 | self.assertFalse(self._called) | ||
312 | 1891 | |||
313 | 1867 | def test_on_password_reset_token_sent_is_not_called(self): | 1892 | def test_on_password_reset_token_sent_is_not_called(self): |
314 | 1868 | """on_password_reset_token_sent is not called if incorrect app_name.""" | 1893 | """on_password_reset_token_sent is not called if incorrect app_name.""" |
315 | 1869 | self.patch(self.ui, 'on_password_reset_token_sent', self._set_called) | 1894 | self.patch(self.ui, 'on_password_reset_token_sent', self._set_called) |
316 | 1870 | 1895 | ||
317 | === modified file 'ubuntu_sso/tests/test_main.py' | |||
318 | --- ubuntu_sso/tests/test_main.py 2010-10-18 17:34:04 +0000 | |||
319 | +++ ubuntu_sso/tests/test_main.py 2010-11-01 21:13:47 +0000 | |||
320 | @@ -152,6 +152,9 @@ | |||
321 | 152 | class FakedAccounts(object): | 152 | class FakedAccounts(object): |
322 | 153 | """Fake the accounts service.""" | 153 | """Fake the accounts service.""" |
323 | 154 | 154 | ||
324 | 155 | def __init__(self): | ||
325 | 156 | self.preferred_email = EMAIL | ||
326 | 157 | |||
327 | 155 | def validate_email(self, email_token): | 158 | def validate_email(self, email_token): |
328 | 156 | """Fake the email validation. Return a fix result.""" | 159 | """Fake the email validation. Return a fix result.""" |
329 | 157 | if email_token is None: | 160 | if email_token is None: |
330 | @@ -164,6 +167,17 @@ | |||
331 | 164 | else: | 167 | else: |
332 | 165 | return STATUS_EMAIL_OK | 168 | return STATUS_EMAIL_OK |
333 | 166 | 169 | ||
334 | 170 | # pylint: disable=E0202, C0103 | ||
335 | 171 | |||
336 | 172 | def me(self): | ||
337 | 173 | """Fake the 'me' information.""" | ||
338 | 174 | return {u'username': u'Wh46bKY', | ||
339 | 175 | u'preferred_email': self.preferred_email, | ||
340 | 176 | u'displayname': u'', | ||
341 | 177 | u'unverified_emails': [u'aaaaaa@example.com'], | ||
342 | 178 | u'verified_emails': [], | ||
343 | 179 | u'openid_identifier': u'Wh46bKY'} | ||
344 | 180 | |||
345 | 167 | 181 | ||
346 | 168 | class FakedSSOServer(object): | 182 | class FakedSSOServer(object): |
347 | 169 | """Fake an SSO server.""" | 183 | """Fake an SSO server.""" |
348 | @@ -279,6 +293,29 @@ | |||
349 | 279 | result = self.processor.login(**self.login_kwargs) | 293 | result = self.processor.login(**self.login_kwargs) |
350 | 280 | self.assertEqual(TOKEN, result, 'authentication was successful.') | 294 | self.assertEqual(TOKEN, result, 'authentication was successful.') |
351 | 281 | 295 | ||
352 | 296 | # is_validated | ||
353 | 297 | |||
354 | 298 | def test_is_validated(self): | ||
355 | 299 | """If preferred email is not None, user is validated.""" | ||
356 | 300 | result = self.processor.is_validated(**self.login_kwargs) | ||
357 | 301 | self.assertTrue(result, 'user must be validated.') | ||
358 | 302 | |||
359 | 303 | def test_is_not_validated(self): | ||
360 | 304 | """If preferred email is None, user is not validated.""" | ||
361 | 305 | service = FakedSSOServer(None, None) | ||
362 | 306 | service.accounts.preferred_email = None | ||
363 | 307 | result = self.processor.is_validated(sso_service=service, | ||
364 | 308 | **self.login_kwargs) | ||
365 | 309 | self.assertFalse(result, 'user must not be validated.') | ||
366 | 310 | |||
367 | 311 | def test_is_not_validated_empty_result(self): | ||
368 | 312 | """If preferred email is None, user is not validated.""" | ||
369 | 313 | service = FakedSSOServer(None, None) | ||
370 | 314 | service.accounts.me = lambda: {} | ||
371 | 315 | result = self.processor.is_validated(sso_service=service, | ||
372 | 316 | **self.login_kwargs) | ||
373 | 317 | self.assertFalse(result, 'user must not be validated.') | ||
374 | 318 | |||
375 | 282 | # validate_email | 319 | # validate_email |
376 | 283 | 320 | ||
377 | 284 | def test_validate_email_if_status_ok(self): | 321 | def test_validate_email_if_status_ok(self): |
378 | @@ -519,8 +556,11 @@ | |||
379 | 519 | def test_login(self): | 556 | def test_login(self): |
380 | 520 | """Test that the login method works ok.""" | 557 | """Test that the login method works ok.""" |
381 | 521 | d = Deferred() | 558 | d = Deferred() |
383 | 522 | self.create_mock_processor().login(EMAIL, PASSWORD, TOKEN_NAME) | 559 | processor = self.create_mock_processor() |
384 | 560 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) | ||
385 | 523 | self.mocker.result(TOKEN) | 561 | self.mocker.result(TOKEN) |
386 | 562 | processor.is_validated(EMAIL, PASSWORD, TOKEN_NAME) | ||
387 | 563 | self.mocker.result(True) | ||
388 | 524 | self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) | 564 | self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) |
389 | 525 | self.mocker.replay() | 565 | self.mocker.replay() |
390 | 526 | 566 | ||
391 | @@ -535,13 +575,40 @@ | |||
392 | 535 | sso_login_processor_class=self.mockprocessorclass) | 575 | sso_login_processor_class=self.mockprocessorclass) |
393 | 536 | self.patch(client, "LoggedIn", verify) | 576 | self.patch(client, "LoggedIn", verify) |
394 | 537 | self.patch(client, "LoginError", d.errback) | 577 | self.patch(client, "LoginError", d.errback) |
395 | 578 | self.patch(client, "UserNotValidated", d.errback) | ||
396 | 579 | client.login(APP_NAME, EMAIL, PASSWORD) | ||
397 | 580 | return d | ||
398 | 581 | |||
399 | 582 | def test_login_user_not_validated(self): | ||
400 | 583 | """Test that the login sends EmailNotValidated signal.""" | ||
401 | 584 | d = Deferred() | ||
402 | 585 | processor = self.create_mock_processor() | ||
403 | 586 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) | ||
404 | 587 | self.mocker.result(TOKEN) | ||
405 | 588 | processor.is_validated(EMAIL, PASSWORD, TOKEN_NAME) | ||
406 | 589 | self.mocker.result(False) | ||
407 | 590 | self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) | ||
408 | 591 | self.mocker.replay() | ||
409 | 592 | |||
410 | 593 | def verify(app_name, email): | ||
411 | 594 | """The actual test.""" | ||
412 | 595 | self.assertEqual(app_name, APP_NAME) | ||
413 | 596 | self.assertEqual(email, EMAIL) | ||
414 | 597 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
415 | 598 | d.callback("Ok") | ||
416 | 599 | |||
417 | 600 | client = SSOLogin(self.mockbusname, | ||
418 | 601 | sso_login_processor_class=self.mockprocessorclass) | ||
419 | 602 | self.patch(client, "LoggedIn", d.errback) | ||
420 | 603 | self.patch(client, "LoginError", d.errback) | ||
421 | 604 | self.patch(client, "UserNotValidated", verify) | ||
422 | 538 | client.login(APP_NAME, EMAIL, PASSWORD) | 605 | client.login(APP_NAME, EMAIL, PASSWORD) |
423 | 539 | return d | 606 | return d |
424 | 540 | 607 | ||
425 | 541 | def test_login_error(self): | 608 | def test_login_error(self): |
426 | 542 | """Test that the login method fails as expected.""" | 609 | """Test that the login method fails as expected.""" |
427 | 543 | d = Deferred() | 610 | d = Deferred() |
429 | 544 | self.mockprocessorclass = self.mocker.mock() | 611 | self.create_mock_processor() |
430 | 545 | self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) | 612 | self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
431 | 546 | 613 | ||
432 | 547 | def fake_gtn(*args): | 614 | def fake_gtn(*args): |
433 | @@ -562,6 +629,7 @@ | |||
434 | 562 | sso_login_processor_class=self.mockprocessorclass) | 629 | sso_login_processor_class=self.mockprocessorclass) |
435 | 563 | self.patch(client, "LoggedIn", d.errback) | 630 | self.patch(client, "LoggedIn", d.errback) |
436 | 564 | self.patch(client, "LoginError", verify) | 631 | self.patch(client, "LoginError", verify) |
437 | 632 | self.patch(client, "UserNotValidated", d.errback) | ||
438 | 565 | client.login(APP_NAME, EMAIL, PASSWORD) | 633 | client.login(APP_NAME, EMAIL, PASSWORD) |
439 | 566 | return d | 634 | return d |
440 | 567 | 635 | ||
441 | @@ -591,7 +659,7 @@ | |||
442 | 591 | def test_validate_email_error(self): | 659 | def test_validate_email_error(self): |
443 | 592 | """Test that the validate_email method fails as expected.""" | 660 | """Test that the validate_email method fails as expected.""" |
444 | 593 | d = Deferred() | 661 | d = Deferred() |
446 | 594 | self.mockprocessorclass = self.mocker.mock() | 662 | self.create_mock_processor() |
447 | 595 | self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) | 663 | self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
448 | 596 | 664 | ||
449 | 597 | def fake_gtn(*args): | 665 | def fake_gtn(*args): |
Works as advertised. This branch is awesome too.