Merge lp:~nataliabidart/ubuntu-sso-client/restore-register-api into lp:ubuntu-sso-client
- restore-register-api
- Merge into trunk
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~nataliabidart/ubuntu-sso-client/restore-register-api | ||||
Merge into: | lp:ubuntu-sso-client | ||||
Diff against target: |
2041 lines (+1387/-28) (has conflicts) 10 files modified
bin/ubuntu-sso-login (+31/-0) data/ui.glade (+78/-1) run-tests (+7/-0) setup.py (+4/-0) ubuntu_sso/gtk/gui.py (+71/-2) ubuntu_sso/gtk/tests/test_gui.py (+65/-25) ubuntu_sso/keyring/linux.py (+50/-0) ubuntu_sso/keyring/tests/test_linux.py (+76/-0) ubuntu_sso/main/linux.py (+453/-0) ubuntu_sso/main/tests/test_linux.py (+552/-0) Text conflict in bin/ubuntu-sso-login Text conflict in data/ui.glade Text conflict in run-tests Text conflict in setup.py Text conflict in ubuntu_sso/gtk/gui.py Text conflict in ubuntu_sso/gtk/tests/test_gui.py Text conflict in ubuntu_sso/keyring/linux.py Text conflict in ubuntu_sso/keyring/tests/test_linux.py Text conflict in ubuntu_sso/main/linux.py Text conflict in ubuntu_sso/main/tests/test_linux.py |
||||
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu-sso-client/restore-register-api | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email: mp+55391@code.launchpad.net |
Commit message
- Restoring former API for register_user. Added a new method call register_with_name to ensure no API is broke (LP: #709494).
Description of the change
To properly test please use the SRU instructions in the attached bug report. Thanks!
Unmerged revisions
- 646. By Natalia Bidart
-
Making UI using the new register_with_name backend metod.
- 645. By Natalia Bidart
-
Restoring former API for register_user. Added a new method call
register_with_name to ensure no API is broke (LP: #709494). - 644. By Natalia Bidart
-
- Register now uses the 'displayname' field to pass it on to SSO as display name (LP: #709494).
- 643. By Natalia Bidart
-
[release] v1.0.8
- 642. By Natalia Bidart
-
* Avoid generating an extra token when attempting to validate a user account (LP: #687523).
- 641. By Natalia Bidart
-
[release] 1.0.7
- 640. By Alejandro J. Cura
-
Store credentials on the keyring *only* from the main thread (LP: #656545)
- 639. By Natalia Bidart
-
The verify email page should be always built, not only on registration.
- 638. By Natalia Bidart
-
[release] 1.0.6.
- 637. By Natalia Bidart
-
* 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.
Preview Diff
1 | === modified file 'bin/ubuntu-sso-login' | |||
2 | --- bin/ubuntu-sso-login 2011-01-11 19:13:19 +0000 | |||
3 | +++ bin/ubuntu-sso-login 2011-03-29 17:35:28 +0000 | |||
4 | @@ -21,6 +21,7 @@ | |||
5 | 21 | 21 | ||
6 | 22 | """Run the dbus service for UserManagement and ApplicationCredentials.""" | 22 | """Run the dbus service for UserManagement and ApplicationCredentials.""" |
7 | 23 | 23 | ||
8 | 24 | <<<<<<< TREE | ||
9 | 24 | # Invalid name "ubuntu-sso-login", pylint: disable=C0103 | 25 | # Invalid name "ubuntu-sso-login", pylint: disable=C0103 |
10 | 25 | 26 | ||
11 | 26 | # import decimal even if we don't need it, pylint: disable=W0611 | 27 | # import decimal even if we don't need it, pylint: disable=W0611 |
12 | @@ -41,6 +42,26 @@ | |||
13 | 41 | # val = globals()[globalname] | 42 | # val = globals()[globalname] |
14 | 42 | # KeyError: 'ROUND_CEiLiNG' | 43 | # KeyError: 'ROUND_CEiLiNG' |
15 | 43 | 44 | ||
16 | 45 | ======= | ||
17 | 46 | # import decimal even if we don't need it. | ||
18 | 47 | import decimal | ||
19 | 48 | # This is a workaround for LP: #467397. Some module in our depency chain sets | ||
20 | 49 | # the locale and imports decimal, and that generates the following trace: | ||
21 | 50 | # Traceback (most recent call last): | ||
22 | 51 | # File "/usr/lib/ubuntu-sso-client/ubuntu-sso-login", line 33 | ||
23 | 52 | # from ubuntu_sso.main import SSOLogin, SSOCredentials | ||
24 | 53 | # File "/usr/lib/pymodules/python2.6/ubuntu_sso/main.py", line 42 | ||
25 | 54 | # from lazr.restfulclient.resource import ServiceRoot | ||
26 | 55 | # File "/usr/lib/python2.6/dist-packages/lazr/restfulclient/resource.py", | ||
27 | 56 | # line 34 | ||
28 | 57 | # import simplejson | ||
29 | 58 | # File "/usr/lib/pymodules/python2.6/simplejson/__init__.py", line 109 | ||
30 | 59 | # from decimal import Decimal | ||
31 | 60 | # File "/usr/lib/python2.6/decimal.py", line 3649, in <module> | ||
32 | 61 | # val = globals()[globalname] | ||
33 | 62 | # KeyError: 'ROUND_CEiLiNG' | ||
34 | 63 | |||
35 | 64 | >>>>>>> MERGE-SOURCE | ||
36 | 44 | import signal | 65 | import signal |
37 | 45 | import sys | 66 | import sys |
38 | 46 | 67 | ||
39 | @@ -67,6 +88,7 @@ | |||
40 | 67 | 88 | ||
41 | 68 | 89 | ||
42 | 69 | def sighup_handler(*a, **kw): | 90 | def sighup_handler(*a, **kw): |
43 | 91 | <<<<<<< TREE | ||
44 | 70 | """Stop the service.""" | 92 | """Stop the service.""" |
45 | 71 | # This handler may be called in any thread, so is not thread safe. | 93 | # This handler may be called in any thread, so is not thread safe. |
46 | 72 | # See the link below for info: | 94 | # See the link below for info: |
47 | @@ -74,6 +96,15 @@ | |||
48 | 74 | # | 96 | # |
49 | 75 | # gtk.main_quit and the logger methods are safe to be called from any | 97 | # gtk.main_quit and the logger methods are safe to be called from any |
50 | 76 | # thread. Just don't call other random stuff here. | 98 | # thread. Just don't call other random stuff here. |
51 | 99 | ======= | ||
52 | 100 | """Stop the service.""" | ||
53 | 101 | # This handler may be called in any thread, so is not thread safe. | ||
54 | 102 | # See the link below for info: | ||
55 | 103 | # www.listware.net/201004/gtk-devel-list/115067-unix-signals-in-glib.html | ||
56 | 104 | # | ||
57 | 105 | # gtk.main_quit and the logger methods are safe to be called from any thread. | ||
58 | 106 | # Just don't call other random stuff here. | ||
59 | 107 | >>>>>>> MERGE-SOURCE | ||
60 | 77 | logger.info("Stoping Ubuntu SSO login manager since SIGHUP was received.") | 108 | logger.info("Stoping Ubuntu SSO login manager since SIGHUP was received.") |
61 | 78 | gtk.main_quit() | 109 | gtk.main_quit() |
62 | 79 | 110 | ||
63 | 80 | 111 | ||
64 | === modified file 'data/ui.glade' | |||
65 | --- data/ui.glade 2010-11-29 16:04:26 +0000 | |||
66 | +++ data/ui.glade 2011-03-29 17:35:28 +0000 | |||
67 | @@ -279,16 +279,42 @@ | |||
68 | 279 | </object> | 279 | </object> |
69 | 280 | <packing> | 280 | <packing> |
70 | 281 | <property name="expand">False</property> | 281 | <property name="expand">False</property> |
71 | 282 | <<<<<<< TREE | ||
72 | 282 | <property name="position">6</property> | 283 | <property name="position">6</property> |
73 | 283 | </packing> | 284 | </packing> |
74 | 284 | </child> | 285 | </child> |
75 | 285 | <child> | 286 | <child> |
76 | 286 | <object class="GtkHBox" id="hbox2"> | 287 | <object class="GtkHBox" id="hbox2"> |
78 | 287 | <property name="visible">True</property> | 288 | ======= |
79 | 289 | <property name="position">10</property> | ||
80 | 290 | </packing> | ||
81 | 291 | </child> | ||
82 | 292 | <child> | ||
83 | 293 | <object class="GtkLabel" id="tc_warning_label"> | ||
84 | 294 | <property name="visible">True</property> | ||
85 | 295 | <property name="wrap">True</property> | ||
86 | 296 | </object> | ||
87 | 297 | <packing> | ||
88 | 298 | <property name="position">11</property> | ||
89 | 299 | </packing> | ||
90 | 300 | </child> | ||
91 | 301 | <child> | ||
92 | 302 | <object class="GtkHBox" id="hbox2"> | ||
93 | 303 | >>>>>>> MERGE-SOURCE | ||
94 | 304 | <property name="visible">True</property> | ||
95 | 305 | <<<<<<< TREE | ||
96 | 288 | <property name="spacing">5</property> | 306 | <property name="spacing">5</property> |
97 | 307 | ======= | ||
98 | 308 | >>>>>>> MERGE-SOURCE | ||
99 | 289 | <child> | 309 | <child> |
100 | 310 | <<<<<<< TREE | ||
101 | 290 | <object class="GtkHButtonBox" id="hbuttonbox9"> | 311 | <object class="GtkHButtonBox" id="hbuttonbox9"> |
102 | 312 | ======= | ||
103 | 313 | <object class="GtkLinkButton" id="login_button"> | ||
104 | 314 | <property name="label">login button</property> | ||
105 | 315 | >>>>>>> MERGE-SOURCE | ||
106 | 291 | <property name="visible">True</property> | 316 | <property name="visible">True</property> |
107 | 317 | <<<<<<< TREE | ||
108 | 292 | <property name="layout_style">start</property> | 318 | <property name="layout_style">start</property> |
109 | 293 | <child> | 319 | <child> |
110 | 294 | <object class="GtkLinkButton" id="login_button"> | 320 | <object class="GtkLinkButton" id="login_button"> |
111 | @@ -305,15 +331,26 @@ | |||
112 | 305 | <property name="position">0</property> | 331 | <property name="position">0</property> |
113 | 306 | </packing> | 332 | </packing> |
114 | 307 | </child> | 333 | </child> |
115 | 334 | ======= | ||
116 | 335 | <property name="can_focus">True</property> | ||
117 | 336 | <property name="receives_default">True</property> | ||
118 | 337 | <property name="relief">none</property> | ||
119 | 338 | <signal name="clicked" handler="on_sign_in_button_clicked" swapped="no"/> | ||
120 | 339 | >>>>>>> MERGE-SOURCE | ||
121 | 308 | </object> | 340 | </object> |
122 | 309 | <packing> | 341 | <packing> |
123 | 310 | <property name="expand">False</property> | 342 | <property name="expand">False</property> |
124 | 343 | <<<<<<< TREE | ||
125 | 344 | ======= | ||
126 | 345 | <property name="fill">True</property> | ||
127 | 346 | >>>>>>> MERGE-SOURCE | ||
128 | 311 | <property name="position">0</property> | 347 | <property name="position">0</property> |
129 | 312 | </packing> | 348 | </packing> |
130 | 313 | </child> | 349 | </child> |
131 | 314 | <child> | 350 | <child> |
132 | 315 | <object class="GtkHButtonBox" id="hbuttonbox1"> | 351 | <object class="GtkHButtonBox" id="hbuttonbox1"> |
133 | 316 | <property name="visible">True</property> | 352 | <property name="visible">True</property> |
134 | 353 | <<<<<<< TREE | ||
135 | 317 | <property name="spacing">5</property> | 354 | <property name="spacing">5</property> |
136 | 318 | <property name="layout_style">end</property> | 355 | <property name="layout_style">end</property> |
137 | 319 | <child> | 356 | <child> |
138 | @@ -345,6 +382,40 @@ | |||
139 | 345 | <property name="position">1</property> | 382 | <property name="position">1</property> |
140 | 346 | </packing> | 383 | </packing> |
141 | 347 | </child> | 384 | </child> |
142 | 385 | ======= | ||
143 | 386 | <property name="can_focus">False</property> | ||
144 | 387 | <property name="spacing">5</property> | ||
145 | 388 | <property name="layout_style">end</property> | ||
146 | 389 | <child> | ||
147 | 390 | <object class="GtkButton" id="join_cancel_button"> | ||
148 | 391 | <property name="label">gtk-cancel</property> | ||
149 | 392 | <property name="visible">True</property> | ||
150 | 393 | <property name="can_focus">True</property> | ||
151 | 394 | <property name="receives_default">True</property> | ||
152 | 395 | <property name="use_stock">True</property> | ||
153 | 396 | </object> | ||
154 | 397 | <packing> | ||
155 | 398 | <property name="expand">False</property> | ||
156 | 399 | <property name="fill">False</property> | ||
157 | 400 | <property name="position">0</property> | ||
158 | 401 | </packing> | ||
159 | 402 | </child> | ||
160 | 403 | <child> | ||
161 | 404 | <object class="GtkButton" id="join_ok_button"> | ||
162 | 405 | <property name="label">gtk-go-forward</property> | ||
163 | 406 | <property name="visible">True</property> | ||
164 | 407 | <property name="can_focus">True</property> | ||
165 | 408 | <property name="receives_default">True</property> | ||
166 | 409 | <property name="use_stock">True</property> | ||
167 | 410 | <signal name="clicked" handler="on_join_ok_button_clicked" swapped="no"/> | ||
168 | 411 | </object> | ||
169 | 412 | <packing> | ||
170 | 413 | <property name="expand">False</property> | ||
171 | 414 | <property name="fill">False</property> | ||
172 | 415 | <property name="position">1</property> | ||
173 | 416 | </packing> | ||
174 | 417 | </child> | ||
175 | 418 | >>>>>>> MERGE-SOURCE | ||
176 | 348 | </object> | 419 | </object> |
177 | 349 | <packing> | 420 | <packing> |
178 | 350 | <property name="expand">False</property> | 421 | <property name="expand">False</property> |
179 | @@ -355,10 +426,16 @@ | |||
180 | 355 | </object> | 426 | </object> |
181 | 356 | <packing> | 427 | <packing> |
182 | 357 | <property name="expand">False</property> | 428 | <property name="expand">False</property> |
183 | 429 | <<<<<<< TREE | ||
184 | 358 | <property name="pack_type">end</property> | 430 | <property name="pack_type">end</property> |
185 | 359 | <property name="position">7</property> | 431 | <property name="position">7</property> |
186 | 360 | </packing> | 432 | </packing> |
187 | 361 | </child> | 433 | </child> |
188 | 434 | ======= | ||
189 | 435 | <property name="position">12</property> | ||
190 | 436 | </packing> | ||
191 | 437 | </child> | ||
192 | 438 | >>>>>>> MERGE-SOURCE | ||
193 | 362 | </object> | 439 | </object> |
194 | 363 | <object class="GtkVBox" id="processing_vbox"> | 440 | <object class="GtkVBox" id="processing_vbox"> |
195 | 364 | <property name="visible">True</property> | 441 | <property name="visible">True</property> |
196 | 365 | 442 | ||
197 | === modified file 'run-tests' | |||
198 | --- run-tests 2011-03-18 09:30:20 +0000 | |||
199 | +++ run-tests 2011-03-29 17:35:28 +0000 | |||
200 | @@ -15,9 +15,16 @@ | |||
201 | 15 | # You should have received a copy of the GNU General Public License along | 15 | # You should have received a copy of the GNU General Public License along |
202 | 16 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
203 | 17 | 17 | ||
204 | 18 | <<<<<<< TREE | ||
205 | 18 | if [ $# -ne 0 ]; then | 19 | if [ $# -ne 0 ]; then |
206 | 19 | # an extra argument was given | 20 | # an extra argument was given |
207 | 20 | MODULE="$@" | 21 | MODULE="$@" |
208 | 22 | ======= | ||
209 | 23 | `which xvfb-run` ./contrib/test "$@" | ||
210 | 24 | pylint ubuntu_sso | ||
211 | 25 | if [ -x `which pep8` ]; then | ||
212 | 26 | pep8 --repeat bin/ contrib/ ubuntu_sso/ | ||
213 | 27 | >>>>>>> MERGE-SOURCE | ||
214 | 21 | else | 28 | else |
215 | 22 | # run all tests, useful for tarmac and reviews | 29 | # run all tests, useful for tarmac and reviews |
216 | 23 | MODULE="ubuntu_sso" | 30 | MODULE="ubuntu_sso" |
217 | 24 | 31 | ||
218 | === modified file 'setup.py' | |||
219 | --- setup.py 2011-03-22 22:32:28 +0000 | |||
220 | +++ setup.py 2011-03-29 17:35:28 +0000 | |||
221 | @@ -86,7 +86,11 @@ | |||
222 | 86 | 86 | ||
223 | 87 | DistUtilsExtra.auto.setup( | 87 | DistUtilsExtra.auto.setup( |
224 | 88 | name='ubuntu-sso-client', | 88 | name='ubuntu-sso-client', |
225 | 89 | <<<<<<< TREE | ||
226 | 89 | version='1.1.12', | 90 | version='1.1.12', |
227 | 91 | ======= | ||
228 | 92 | version='1.0.8', | ||
229 | 93 | >>>>>>> MERGE-SOURCE | ||
230 | 90 | license='GPL v3', | 94 | license='GPL v3', |
231 | 91 | author='Natalia Bidart', | 95 | author='Natalia Bidart', |
232 | 92 | author_email='natalia.bidart@canonical.com', | 96 | author_email='natalia.bidart@canonical.com', |
233 | 93 | 97 | ||
234 | === modified file 'ubuntu_sso/gtk/gui.py' | |||
235 | --- ubuntu_sso/gtk/gui.py 2011-03-16 01:36:07 +0000 | |||
236 | +++ ubuntu_sso/gtk/gui.py 2011-03-29 17:35:28 +0000 | |||
237 | @@ -30,7 +30,13 @@ | |||
238 | 30 | 30 | ||
239 | 31 | import dbus | 31 | import dbus |
240 | 32 | import gettext | 32 | import gettext |
241 | 33 | <<<<<<< TREE | ||
242 | 33 | import gtk | 34 | import gtk |
243 | 35 | ======= | ||
244 | 36 | import gobject | ||
245 | 37 | import gtk # pylint: disable=W0403 | ||
246 | 38 | import webkit | ||
247 | 39 | >>>>>>> MERGE-SOURCE | ||
248 | 34 | import xdg | 40 | import xdg |
249 | 35 | 41 | ||
250 | 36 | from dbus.mainloop.glib import DBusGMainLoop | 42 | from dbus.mainloop.glib import DBusGMainLoop |
251 | @@ -125,7 +131,7 @@ | |||
252 | 125 | self.is_password = is_password | 131 | self.is_password = is_password |
253 | 126 | self.warning = None | 132 | self.warning = None |
254 | 127 | 133 | ||
256 | 128 | super(LabeledEntry, self).__init__(*args, **kwargs) | 134 | gtk.Entry.__init__(self, *args, **kwargs) |
257 | 129 | 135 | ||
258 | 130 | self.set_width_chars(DEFAULT_WIDTH) | 136 | self.set_width_chars(DEFAULT_WIDTH) |
259 | 131 | self._set_label(self, None) | 137 | self._set_label(self, None) |
260 | @@ -161,7 +167,7 @@ | |||
261 | 161 | 167 | ||
262 | 162 | def get_text(self): | 168 | def get_text(self): |
263 | 163 | """Get text only if it's not the label nor empty.""" | 169 | """Get text only if it's not the label nor empty.""" |
265 | 164 | result = super(LabeledEntry, self).get_text() | 170 | result = gtk.Entry.get_text(self) |
266 | 165 | if result == self.label or result.isspace(): | 171 | if result == self.label or result.isspace(): |
267 | 166 | result = '' | 172 | result = '' |
268 | 167 | return result | 173 | return result |
269 | @@ -255,6 +261,7 @@ | |||
270 | 255 | self.app_label = '<b>%s</b>' % self.app_name | 261 | self.app_label = '<b>%s</b>' % self.app_name |
271 | 256 | self.tc_url = tc_url | 262 | self.tc_url = tc_url |
272 | 257 | self.help_text = help_text | 263 | self.help_text = help_text |
273 | 264 | <<<<<<< TREE | ||
274 | 258 | self.login_only = login_only | 265 | self.login_only = login_only |
275 | 259 | 266 | ||
276 | 260 | self.close_callback = NO_OP | 267 | self.close_callback = NO_OP |
277 | @@ -264,6 +271,11 @@ | |||
278 | 264 | 271 | ||
279 | 265 | self.user_email = None | 272 | self.user_email = None |
280 | 266 | self.user_password = None | 273 | self.user_password = None |
281 | 274 | ======= | ||
282 | 275 | self.close_callback = close_callback | ||
283 | 276 | self.user_email = None | ||
284 | 277 | self.user_password = None | ||
285 | 278 | >>>>>>> MERGE-SOURCE | ||
286 | 267 | 279 | ||
287 | 268 | ui_filename = get_data_file('ui.glade') | 280 | ui_filename = get_data_file('ui.glade') |
288 | 269 | builder = gtk.Builder() | 281 | builder = gtk.Builder() |
289 | @@ -325,7 +337,35 @@ | |||
290 | 325 | self.request_password_token_vbox, | 337 | self.request_password_token_vbox, |
291 | 326 | self.set_new_password_vbox) | 338 | self.set_new_password_vbox) |
292 | 327 | 339 | ||
293 | 340 | <<<<<<< TREE | ||
294 | 328 | self._append_pages() | 341 | self._append_pages() |
295 | 342 | ======= | ||
296 | 343 | self._append_page(self._build_processing_page()) | ||
297 | 344 | self._append_page(self._build_success_page()) | ||
298 | 345 | self._append_page(self._build_login_page()) | ||
299 | 346 | self._append_page(self._build_request_password_token_page()) | ||
300 | 347 | self._append_page(self._build_set_new_password_page()) | ||
301 | 348 | self._append_page(self._build_verify_email_page()) | ||
302 | 349 | |||
303 | 350 | window_size = None | ||
304 | 351 | if not login_only: | ||
305 | 352 | window_size = (550, 500) | ||
306 | 353 | self._append_page(self._build_enter_details_page()) | ||
307 | 354 | self._append_page(self._build_tc_page()) | ||
308 | 355 | self.login_button.grab_focus() | ||
309 | 356 | self._set_current_page(self.enter_details_vbox) | ||
310 | 357 | else: | ||
311 | 358 | window_size = (400, 350) | ||
312 | 359 | self.login_back_button.hide() | ||
313 | 360 | self.login_ok_button.grab_focus() | ||
314 | 361 | self.login_vbox.help_text = help_text | ||
315 | 362 | self._set_current_page(self.login_vbox) | ||
316 | 363 | |||
317 | 364 | self.window.set_size_request(*window_size) | ||
318 | 365 | size_req = (int(window_size[0] * 0.9), -1) | ||
319 | 366 | for label in self.labels: | ||
320 | 367 | label.set_size_request(*size_req) | ||
321 | 368 | >>>>>>> MERGE-SOURCE | ||
322 | 329 | 369 | ||
323 | 330 | self._signals = { | 370 | self._signals = { |
324 | 331 | 'CaptchaGenerated': | 371 | 'CaptchaGenerated': |
325 | @@ -750,12 +790,27 @@ | |||
326 | 750 | if self.window is not None: | 790 | if self.window is not None: |
327 | 751 | self.window.hide() | 791 | self.window.hide() |
328 | 752 | 792 | ||
329 | 793 | <<<<<<< TREE | ||
330 | 753 | # process any pending events before callbacking with result | 794 | # process any pending events before callbacking with result |
331 | 754 | while gtk.events_pending(): | 795 | while gtk.events_pending(): |
332 | 755 | gtk.main_iteration() | 796 | gtk.main_iteration() |
333 | 756 | 797 | ||
334 | 757 | if not self._done: | 798 | if not self._done: |
335 | 758 | self.user_cancellation_callback(self.app_name) | 799 | self.user_cancellation_callback(self.app_name) |
336 | 800 | ======= | ||
337 | 801 | # process any pending events before emitting signals | ||
338 | 802 | while gtk.events_pending(): | ||
339 | 803 | gtk.main_iteration() | ||
340 | 804 | |||
341 | 805 | if len(args) > 0 and args[0] in self.cancels: | ||
342 | 806 | self.window.emit(SIG_USER_CANCELATION, self.app_name) | ||
343 | 807 | elif len(self._gtk_signal_log) > 0: | ||
344 | 808 | signal = self._gtk_signal_log[-1][0] | ||
345 | 809 | args = self._gtk_signal_log[-1][1:] | ||
346 | 810 | self.window.emit(signal, *args) | ||
347 | 811 | else: | ||
348 | 812 | self.window.emit(SIG_USER_CANCELATION, self.app_name) | ||
349 | 813 | >>>>>>> MERGE-SOURCE | ||
350 | 759 | 814 | ||
351 | 760 | # call user defined callback | 815 | # call user defined callback |
352 | 761 | logger.info('Calling custom close_callback %r with params %r, %r', | 816 | logger.info('Calling custom close_callback %r with params %r, %r', |
353 | @@ -816,12 +871,21 @@ | |||
354 | 816 | self.user_email = email1 | 871 | self.user_email = email1 |
355 | 817 | self.user_password = password1 | 872 | self.user_password = password1 |
356 | 818 | 873 | ||
357 | 874 | <<<<<<< TREE | ||
358 | 819 | logger.info('Calling register_user with email %r, password <hidden>,' \ | 875 | logger.info('Calling register_user with email %r, password <hidden>,' \ |
359 | 820 | ' name %r, captcha_id %r and captcha_solution %r.', email1, | 876 | ' name %r, captcha_id %r and captcha_solution %r.', email1, |
360 | 821 | name, self._captcha_id, captcha_solution) | 877 | name, self._captcha_id, captcha_solution) |
361 | 822 | f = self.backend.register_user | 878 | f = self.backend.register_user |
362 | 823 | f(self.app_name, email1, password1, name, | 879 | f(self.app_name, email1, password1, name, |
363 | 824 | self._captcha_id, captcha_solution, | 880 | self._captcha_id, captcha_solution, |
364 | 881 | ======= | ||
365 | 882 | logger.info('Calling register_with_name with email %r, password, ' | ||
366 | 883 | '<hidden> name %r, captcha_id %r and captcha_solution %r.', | ||
367 | 884 | email1, name, self._captcha_id, captcha_solution) | ||
368 | 885 | f = self.backend.register_with_name | ||
369 | 886 | f(self.app_name, email1, password1, name, | ||
370 | 887 | self._captcha_id, captcha_solution, | ||
371 | 888 | >>>>>>> MERGE-SOURCE | ||
372 | 825 | reply_handler=NO_OP, error_handler=NO_OP) | 889 | reply_handler=NO_OP, error_handler=NO_OP) |
373 | 826 | 890 | ||
374 | 827 | def on_verify_token_button_clicked(self, *args, **kwargs): | 891 | def on_verify_token_button_clicked(self, *args, **kwargs): |
375 | @@ -1122,6 +1186,11 @@ | |||
376 | 1122 | self.on_user_registered(app_name, email) | 1186 | self.on_user_registered(app_name, email) |
377 | 1123 | 1187 | ||
378 | 1124 | @log_call | 1188 | @log_call |
379 | 1189 | def on_user_not_validated(self, app_name, email, *args, **kwargs): | ||
380 | 1190 | """User was not validated.""" | ||
381 | 1191 | self.on_user_registered(app_name, email) | ||
382 | 1192 | |||
383 | 1193 | @log_call | ||
384 | 1125 | def on_password_reset_token_sent(self, app_name, email, *args, **kwargs): | 1194 | def on_password_reset_token_sent(self, app_name, email, *args, **kwargs): |
385 | 1126 | """Password reset token was successfully sent.""" | 1195 | """Password reset token was successfully sent.""" |
386 | 1127 | msg = self.SET_NEW_PASSWORD_LABEL % {'email': email} | 1196 | msg = self.SET_NEW_PASSWORD_LABEL % {'email': email} |
387 | 1128 | 1197 | ||
388 | === modified file 'ubuntu_sso/gtk/tests/test_gui.py' | |||
389 | --- ubuntu_sso/gtk/tests/test_gui.py 2011-03-16 01:36:07 +0000 | |||
390 | +++ ubuntu_sso/gtk/tests/test_gui.py 2011-03-29 17:35:28 +0000 | |||
391 | @@ -51,7 +51,7 @@ | |||
392 | 51 | self._args = args | 51 | self._args = args |
393 | 52 | self._kwargs = kwargs | 52 | self._kwargs = kwargs |
394 | 53 | self._called = {} | 53 | self._called = {} |
396 | 54 | for i in ('generate_captcha', 'login', 'register_user', | 54 | for i in ('generate_captcha', 'login', 'register_with_name', |
397 | 55 | 'validate_email', 'request_password_reset_token', | 55 | 'validate_email', 'request_password_reset_token', |
398 | 56 | 'set_new_password'): | 56 | 'set_new_password'): |
399 | 57 | setattr(self, i, self._record_call(i)) | 57 | setattr(self, i, self._record_call(i)) |
400 | @@ -709,8 +709,8 @@ | |||
401 | 709 | """Clicking 'join_ok_button' sends info to backend using 'register'.""" | 709 | """Clicking 'join_ok_button' sends info to backend using 'register'.""" |
402 | 710 | self.click_join_with_valid_data() | 710 | self.click_join_with_valid_data() |
403 | 711 | 711 | ||
406 | 712 | # assert register_user was called | 712 | # assert register_with_name was called |
407 | 713 | expected = 'register_user' | 713 | expected = 'register_with_name' |
408 | 714 | self.assertIn(expected, self.ui.backend._called) | 714 | self.assertIn(expected, self.ui.backend._called) |
409 | 715 | self.assertEqual(self.ui.backend._called[expected], | 715 | self.assertEqual(self.ui.backend._called[expected], |
410 | 716 | ((APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, | 716 | ((APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, |
411 | @@ -866,6 +866,7 @@ | |||
412 | 866 | self.ui.join_ok_button.clicked() | 866 | self.ui.join_ok_button.clicked() |
413 | 867 | self.assertTrue(self._called) | 867 | self.assertTrue(self._called) |
414 | 868 | 868 | ||
415 | 869 | <<<<<<< TREE | ||
416 | 869 | def test_user_and_pass_are_cached(self): | 870 | def test_user_and_pass_are_cached(self): |
417 | 870 | """Username and password are temporarly cached for further use.""" | 871 | """Username and password are temporarly cached for further use.""" |
418 | 871 | self.click_join_with_valid_data() | 872 | self.click_join_with_valid_data() |
419 | @@ -880,6 +881,14 @@ | |||
420 | 880 | self.ui.CAPTCHA_LOAD_ERROR) | 881 | self.ui.CAPTCHA_LOAD_ERROR) |
421 | 881 | self.assertEqual(self._called, ((), {})) | 882 | self.assertEqual(self._called, ((), {})) |
422 | 882 | 883 | ||
423 | 884 | ======= | ||
424 | 885 | def test_user_and_pass_are_cached(self): | ||
425 | 886 | """Username and password are temporarly cached for further use.""" | ||
426 | 887 | self.click_join_with_valid_data() | ||
427 | 888 | self.assertEqual(self.ui.user_email, EMAIL) | ||
428 | 889 | self.assertEqual(self.ui.user_password, PASSWORD) | ||
429 | 890 | |||
430 | 891 | >>>>>>> MERGE-SOURCE | ||
431 | 883 | 892 | ||
432 | 884 | class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase): | 893 | class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase): |
433 | 885 | """Test suite for the user registration (with no t&c link).""" | 894 | """Test suite for the user registration (with no t&c link).""" |
434 | @@ -1282,20 +1291,37 @@ | |||
435 | 1282 | self.assert_warnings_visibility() | 1291 | self.assert_warnings_visibility() |
436 | 1283 | 1292 | ||
437 | 1284 | 1293 | ||
452 | 1285 | class VerifyEmailLoginOnlyTestCase(VerifyEmailTestCase): | 1294 | <<<<<<< TREE |
453 | 1286 | """Test suite for the user login (verify email page).""" | 1295 | class VerifyEmailLoginOnlyTestCase(VerifyEmailTestCase): |
454 | 1287 | 1296 | """Test suite for the user login (verify email page).""" | |
455 | 1288 | kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT, | 1297 | |
456 | 1289 | login_only=True) | 1298 | kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT, |
457 | 1290 | 1299 | login_only=True) | |
458 | 1291 | 1300 | ||
459 | 1292 | class VerifyEmailValidationLoginOnlyTestCase(VerifyEmailValidationTestCase): | 1301 | |
460 | 1293 | """Test suite for the user login validation (verify email page).""" | 1302 | class VerifyEmailValidationLoginOnlyTestCase(VerifyEmailValidationTestCase): |
461 | 1294 | 1303 | """Test suite for the user login validation (verify email page).""" | |
462 | 1295 | kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT, | 1304 | |
463 | 1296 | login_only=True) | 1305 | kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT, |
464 | 1297 | 1306 | login_only=True) | |
465 | 1298 | 1307 | ||
466 | 1308 | |||
467 | 1309 | ======= | ||
468 | 1310 | class VerifyEmailLoginOnlyTestCase(VerifyEmailTestCase): | ||
469 | 1311 | """Test suite for the user login (verify email page).""" | ||
470 | 1312 | |||
471 | 1313 | kwargs = dict(app_name=APP_NAME, tc_uri=TC_URI, help_text=HELP_TEXT, | ||
472 | 1314 | login_only=True) | ||
473 | 1315 | |||
474 | 1316 | |||
475 | 1317 | class VerifyEmailValidationLoginOnlyTestCase(VerifyEmailValidationTestCase): | ||
476 | 1318 | """Test suite for the user login validation (verify email page).""" | ||
477 | 1319 | |||
478 | 1320 | kwargs = dict(app_name=APP_NAME, tc_uri=TC_URI, help_text=HELP_TEXT, | ||
479 | 1321 | login_only=True) | ||
480 | 1322 | |||
481 | 1323 | |||
482 | 1324 | >>>>>>> MERGE-SOURCE | ||
483 | 1299 | class RegistrationValidationTestCase(UbuntuSSOClientTestCase): | 1325 | class RegistrationValidationTestCase(UbuntuSSOClientTestCase): |
484 | 1300 | """Test suite for the user registration validations.""" | 1326 | """Test suite for the user registration validations.""" |
485 | 1301 | 1327 | ||
486 | @@ -1312,8 +1338,13 @@ | |||
487 | 1312 | 1338 | ||
488 | 1313 | self.assert_correct_entry_warning(self.ui.name_entry, | 1339 | self.assert_correct_entry_warning(self.ui.name_entry, |
489 | 1314 | self.ui.FIELD_REQUIRED) | 1340 | self.ui.FIELD_REQUIRED) |
490 | 1341 | <<<<<<< TREE | ||
491 | 1315 | self.assertNotIn('register_user', self.ui.backend._called) | 1342 | self.assertNotIn('register_user', self.ui.backend._called) |
492 | 1316 | 1343 | ||
493 | 1344 | ======= | ||
494 | 1345 | self.assertNotIn('register_with_name', self.ui.backend._called) | ||
495 | 1346 | |||
496 | 1347 | >>>>>>> MERGE-SOURCE | ||
497 | 1317 | def test_warning_is_shown_if_empty_email(self): | 1348 | def test_warning_is_shown_if_empty_email(self): |
498 | 1318 | """A warning message is shown if emails are empty.""" | 1349 | """A warning message is shown if emails are empty.""" |
499 | 1319 | self.ui.email1_entry.set_text('') | 1350 | self.ui.email1_entry.set_text('') |
500 | @@ -1325,7 +1356,7 @@ | |||
501 | 1325 | self.ui.FIELD_REQUIRED) | 1356 | self.ui.FIELD_REQUIRED) |
502 | 1326 | self.assert_correct_entry_warning(self.ui.email2_entry, | 1357 | self.assert_correct_entry_warning(self.ui.email2_entry, |
503 | 1327 | self.ui.FIELD_REQUIRED) | 1358 | self.ui.FIELD_REQUIRED) |
505 | 1328 | self.assertNotIn('register_user', self.ui.backend._called) | 1359 | self.assertNotIn('register_with_name', self.ui.backend._called) |
506 | 1329 | 1360 | ||
507 | 1330 | def test_warning_is_shown_if_email_mismatch(self): | 1361 | def test_warning_is_shown_if_email_mismatch(self): |
508 | 1331 | """A warning message is shown if emails doesn't match.""" | 1362 | """A warning message is shown if emails doesn't match.""" |
509 | @@ -1338,7 +1369,7 @@ | |||
510 | 1338 | self.ui.EMAIL_MISMATCH) | 1369 | self.ui.EMAIL_MISMATCH) |
511 | 1339 | self.assert_correct_entry_warning(self.ui.email2_entry, | 1370 | self.assert_correct_entry_warning(self.ui.email2_entry, |
512 | 1340 | self.ui.EMAIL_MISMATCH) | 1371 | self.ui.EMAIL_MISMATCH) |
514 | 1341 | self.assertNotIn('register_user', self.ui.backend._called) | 1372 | self.assertNotIn('register_with_name', self.ui.backend._called) |
515 | 1342 | 1373 | ||
516 | 1343 | def test_warning_is_shown_if_invalid_email(self): | 1374 | def test_warning_is_shown_if_invalid_email(self): |
517 | 1344 | """A warning message is shown if email is invalid.""" | 1375 | """A warning message is shown if email is invalid.""" |
518 | @@ -1351,7 +1382,7 @@ | |||
519 | 1351 | self.ui.EMAIL_INVALID) | 1382 | self.ui.EMAIL_INVALID) |
520 | 1352 | self.assert_correct_entry_warning(self.ui.email2_entry, | 1383 | self.assert_correct_entry_warning(self.ui.email2_entry, |
521 | 1353 | self.ui.EMAIL_INVALID) | 1384 | self.ui.EMAIL_INVALID) |
523 | 1354 | self.assertNotIn('register_user', self.ui.backend._called) | 1385 | self.assertNotIn('register_with_name', self.ui.backend._called) |
524 | 1355 | 1386 | ||
525 | 1356 | def test_password_help_is_always_shown(self): | 1387 | def test_password_help_is_always_shown(self): |
526 | 1357 | """Password help text is correctly displayed.""" | 1388 | """Password help text is correctly displayed.""" |
527 | @@ -1359,7 +1390,7 @@ | |||
528 | 1359 | 'password help text is visible.') | 1390 | 'password help text is visible.') |
529 | 1360 | self.assertEqual(self.ui.password_help_label.get_text(), | 1391 | self.assertEqual(self.ui.password_help_label.get_text(), |
530 | 1361 | self.ui.PASSWORD_HELP) | 1392 | self.ui.PASSWORD_HELP) |
532 | 1362 | self.assertNotIn('register_user', self.ui.backend._called) | 1393 | self.assertNotIn('register_with_name', self.ui.backend._called) |
533 | 1363 | 1394 | ||
534 | 1364 | def test_warning_is_shown_if_password_mismatch(self): | 1395 | def test_warning_is_shown_if_password_mismatch(self): |
535 | 1365 | """A warning message is shown if password doesn't match.""" | 1396 | """A warning message is shown if password doesn't match.""" |
536 | @@ -1372,7 +1403,7 @@ | |||
537 | 1372 | self.ui.PASSWORD_MISMATCH) | 1403 | self.ui.PASSWORD_MISMATCH) |
538 | 1373 | self.assert_correct_entry_warning(self.ui.password2_entry, | 1404 | self.assert_correct_entry_warning(self.ui.password2_entry, |
539 | 1374 | self.ui.PASSWORD_MISMATCH) | 1405 | self.ui.PASSWORD_MISMATCH) |
541 | 1375 | self.assertNotIn('register_user', self.ui.backend._called) | 1406 | self.assertNotIn('register_with_name', self.ui.backend._called) |
542 | 1376 | 1407 | ||
543 | 1377 | def test_warning_is_shown_if_password_too_weak(self): | 1408 | def test_warning_is_shown_if_password_too_weak(self): |
544 | 1378 | """A warning message is shown if password is too weak.""" | 1409 | """A warning message is shown if password is too weak.""" |
545 | @@ -1387,7 +1418,7 @@ | |||
546 | 1387 | self.ui.PASSWORD_TOO_WEAK) | 1418 | self.ui.PASSWORD_TOO_WEAK) |
547 | 1388 | self.assert_correct_entry_warning(self.ui.password2_entry, | 1419 | self.assert_correct_entry_warning(self.ui.password2_entry, |
548 | 1389 | self.ui.PASSWORD_TOO_WEAK) | 1420 | self.ui.PASSWORD_TOO_WEAK) |
550 | 1390 | self.assertNotIn('register_user', self.ui.backend._called) | 1421 | self.assertNotIn('register_with_name', self.ui.backend._called) |
551 | 1391 | 1422 | ||
552 | 1392 | def test_warning_is_shown_if_tc_not_accepted(self): | 1423 | def test_warning_is_shown_if_tc_not_accepted(self): |
553 | 1393 | """A warning message is shown if TC are not accepted.""" | 1424 | """A warning message is shown if TC are not accepted.""" |
554 | @@ -1398,7 +1429,7 @@ | |||
555 | 1398 | 1429 | ||
556 | 1399 | self.assert_correct_label_warning(self.ui.tc_warning_label, | 1430 | self.assert_correct_label_warning(self.ui.tc_warning_label, |
557 | 1400 | self.ui.TC_NOT_ACCEPTED) | 1431 | self.ui.TC_NOT_ACCEPTED) |
559 | 1401 | self.assertNotIn('register_user', self.ui.backend._called) | 1432 | self.assertNotIn('register_with_name', self.ui.backend._called) |
560 | 1402 | 1433 | ||
561 | 1403 | def test_warning_is_shown_if_not_captcha_solution(self): | 1434 | def test_warning_is_shown_if_not_captcha_solution(self): |
562 | 1404 | """A warning message is shown if TC are not accepted.""" | 1435 | """A warning message is shown if TC are not accepted.""" |
563 | @@ -1409,7 +1440,7 @@ | |||
564 | 1409 | 1440 | ||
565 | 1410 | self.assert_correct_entry_warning(self.ui.captcha_solution_entry, | 1441 | self.assert_correct_entry_warning(self.ui.captcha_solution_entry, |
566 | 1411 | self.ui.FIELD_REQUIRED) | 1442 | self.ui.FIELD_REQUIRED) |
568 | 1412 | self.assertNotIn('register_user', self.ui.backend._called) | 1443 | self.assertNotIn('register_with_name', self.ui.backend._called) |
569 | 1413 | 1444 | ||
570 | 1414 | def test_no_warning_messages_if_valid_data(self): | 1445 | def test_no_warning_messages_if_valid_data(self): |
571 | 1415 | """No warning messages are shown if the data is valid.""" | 1446 | """No warning messages are shown if the data is valid.""" |
572 | @@ -1548,6 +1579,7 @@ | |||
573 | 1548 | self.ui.login_ok_button.clicked() | 1579 | self.ui.login_ok_button.clicked() |
574 | 1549 | self.assertTrue(self._called) | 1580 | self.assertTrue(self._called) |
575 | 1550 | 1581 | ||
576 | 1582 | <<<<<<< TREE | ||
577 | 1551 | def test_user_and_pass_are_cached(self): | 1583 | def test_user_and_pass_are_cached(self): |
578 | 1552 | """Username and password are temporarly cached for further use.""" | 1584 | """Username and password are temporarly cached for further use.""" |
579 | 1553 | self.click_connect_with_valid_data() | 1585 | self.click_connect_with_valid_data() |
580 | @@ -1580,6 +1612,14 @@ | |||
581 | 1580 | 1612 | ||
582 | 1581 | self.assertEqual(self._called, ((), {})) | 1613 | self.assertEqual(self._called, ((), {})) |
583 | 1582 | 1614 | ||
584 | 1615 | ======= | ||
585 | 1616 | def test_user_and_pass_are_cached(self): | ||
586 | 1617 | """Username and password are temporarly cached for further use.""" | ||
587 | 1618 | self.click_connect_with_valid_data() | ||
588 | 1619 | self.assertEqual(self.ui.user_email, EMAIL) | ||
589 | 1620 | self.assertEqual(self.ui.user_password, PASSWORD) | ||
590 | 1621 | |||
591 | 1622 | >>>>>>> MERGE-SOURCE | ||
592 | 1583 | 1623 | ||
593 | 1584 | class LoginValidationTestCase(UbuntuSSOClientTestCase): | 1624 | class LoginValidationTestCase(UbuntuSSOClientTestCase): |
594 | 1585 | """Test suite for the user login validation.""" | 1625 | """Test suite for the user login validation.""" |
595 | 1586 | 1626 | ||
596 | === modified file 'ubuntu_sso/keyring/linux.py' | |||
597 | --- ubuntu_sso/keyring/linux.py 2011-03-03 15:19:29 +0000 | |||
598 | +++ ubuntu_sso/keyring/linux.py 2011-03-29 17:35:28 +0000 | |||
599 | @@ -29,6 +29,7 @@ | |||
600 | 29 | from twisted.internet.defer import inlineCallbacks, returnValue | 29 | from twisted.internet.defer import inlineCallbacks, returnValue |
601 | 30 | 30 | ||
602 | 31 | from ubuntu_sso.logger import setup_logging | 31 | from ubuntu_sso.logger import setup_logging |
603 | 32 | <<<<<<< TREE | ||
604 | 32 | from ubuntu_sso.utils.txsecrets import SecretService | 33 | from ubuntu_sso.utils.txsecrets import SecretService |
605 | 33 | from ubuntu_sso.keyring import ( | 34 | from ubuntu_sso.keyring import ( |
606 | 34 | get_token_name, | 35 | get_token_name, |
607 | @@ -38,17 +39,58 @@ | |||
608 | 38 | 39 | ||
609 | 39 | 40 | ||
610 | 40 | logger = setup_logging("ubuntu_sso.keyring") | 41 | logger = setup_logging("ubuntu_sso.keyring") |
611 | 42 | ======= | ||
612 | 43 | |||
613 | 44 | |||
614 | 45 | logger = setup_logging("ubuntu_sso.keyring") | ||
615 | 46 | TOKEN_SEPARATOR = ' @ ' | ||
616 | 47 | SEPARATOR_REPLACEMENT = ' AT ' | ||
617 | 48 | |||
618 | 49 | U1_APP_NAME = "Ubuntu One" | ||
619 | 50 | U1_KEY_NAME = "UbuntuOne token for https://ubuntuone.com" | ||
620 | 51 | U1_KEY_ATTR = { | ||
621 | 52 | "oauth-consumer-key": "ubuntuone", | ||
622 | 53 | "ubuntuone-realm": "https://ubuntuone.com", | ||
623 | 54 | } | ||
624 | 55 | |||
625 | 56 | |||
626 | 57 | def get_old_token_name(app_name): | ||
627 | 58 | """Build the token name (old style).""" | ||
628 | 59 | quoted_app_name = urllib.quote(app_name) | ||
629 | 60 | computer_name = socket.gethostname() | ||
630 | 61 | quoted_computer_name = urllib.quote(computer_name) | ||
631 | 62 | return "%s - %s" % (quoted_app_name, quoted_computer_name) | ||
632 | 63 | |||
633 | 64 | |||
634 | 65 | def get_token_name(app_name): | ||
635 | 66 | """Build the token name.""" | ||
636 | 67 | computer_name = socket.gethostname() | ||
637 | 68 | computer_name = computer_name.replace(TOKEN_SEPARATOR, | ||
638 | 69 | SEPARATOR_REPLACEMENT) | ||
639 | 70 | return TOKEN_SEPARATOR.join((app_name, computer_name)).encode('utf-8') | ||
640 | 71 | >>>>>>> MERGE-SOURCE | ||
641 | 41 | 72 | ||
642 | 42 | 73 | ||
643 | 43 | class Keyring(object): | 74 | class Keyring(object): |
644 | 44 | """A Keyring for a given application name.""" | 75 | """A Keyring for a given application name.""" |
645 | 45 | 76 | ||
646 | 77 | <<<<<<< TREE | ||
647 | 46 | def __init__(self): | 78 | def __init__(self): |
648 | 47 | """Initialize this instance.""" | 79 | """Initialize this instance.""" |
649 | 48 | self.service = SecretService() | 80 | self.service = SecretService() |
650 | 49 | 81 | ||
651 | 50 | @inlineCallbacks | 82 | @inlineCallbacks |
652 | 51 | def _find_keyring_item(self, app_name, attr=None): | 83 | def _find_keyring_item(self, app_name, attr=None): |
653 | 84 | ======= | ||
654 | 85 | def __init__(self, app_name): | ||
655 | 86 | """Initialize this instance given the app_name.""" | ||
656 | 87 | if not gnomekeyring.is_available(): | ||
657 | 88 | raise gnomekeyring.NoKeyringDaemonError | ||
658 | 89 | self.app_name = app_name | ||
659 | 90 | self.token_name = get_token_name(self.app_name) | ||
660 | 91 | |||
661 | 92 | def _find_keyring_item(self, attr=None): | ||
662 | 93 | >>>>>>> MERGE-SOURCE | ||
663 | 52 | """Return the keyring item or None if not found.""" | 94 | """Return the keyring item or None if not found.""" |
664 | 53 | if attr is None: | 95 | if attr is None: |
665 | 54 | logger.debug("getting attr") | 96 | logger.debug("getting attr") |
666 | @@ -75,11 +117,19 @@ | |||
667 | 75 | # Creates the secret from the credentials | 117 | # Creates the secret from the credentials |
668 | 76 | secret = urllib.urlencode(cred) | 118 | secret = urllib.urlencode(cred) |
669 | 77 | 119 | ||
670 | 120 | <<<<<<< TREE | ||
671 | 78 | attr = self._get_keyring_attr(app_name) | 121 | attr = self._get_keyring_attr(app_name) |
672 | 122 | ======= | ||
673 | 123 | >>>>>>> MERGE-SOURCE | ||
674 | 79 | # Add our SSO credentials to the keyring | 124 | # Add our SSO credentials to the keyring |
675 | 125 | <<<<<<< TREE | ||
676 | 80 | yield self.service.open_session() | 126 | yield self.service.open_session() |
677 | 81 | collection = yield self.service.get_default_collection() | 127 | collection = yield self.service.get_default_collection() |
678 | 82 | yield collection.create_item(app_name, attr, secret, True) | 128 | yield collection.create_item(app_name, attr, secret, True) |
679 | 129 | ======= | ||
680 | 130 | gnomekeyring.item_create_sync(None, gnomekeyring.ITEM_GENERIC_SECRET, | ||
681 | 131 | self.app_name, self._get_keyring_attr(), secret, True) | ||
682 | 132 | >>>>>>> MERGE-SOURCE | ||
683 | 83 | 133 | ||
684 | 84 | @inlineCallbacks | 134 | @inlineCallbacks |
685 | 85 | def _migrate_old_token_name(self, app_name): | 135 | def _migrate_old_token_name(self, app_name): |
686 | 86 | 136 | ||
687 | === modified file 'ubuntu_sso/keyring/tests/test_linux.py' | |||
688 | --- ubuntu_sso/keyring/tests/test_linux.py 2011-03-18 09:30:20 +0000 | |||
689 | +++ ubuntu_sso/keyring/tests/test_linux.py 2011-03-29 17:35:28 +0000 | |||
690 | @@ -65,6 +65,7 @@ | |||
691 | 65 | return True | 65 | return True |
692 | 66 | 66 | ||
693 | 67 | 67 | ||
694 | 68 | <<<<<<< TREE | ||
695 | 68 | class MockCollection(object): | 69 | class MockCollection(object): |
696 | 69 | """A collection of items containing secrets.""" | 70 | """A collection of items containing secrets.""" |
697 | 70 | 71 | ||
698 | @@ -114,6 +115,44 @@ | |||
699 | 114 | if len(self.collections) == 0: | 115 | if len(self.collections) == 0: |
700 | 115 | self.create_collection("default") | 116 | self.create_collection("default") |
701 | 116 | return defer.succeed(self.collections["default"]) | 117 | return defer.succeed(self.collections["default"]) |
702 | 118 | ======= | ||
703 | 119 | class MockGnomeKeyring(object): | ||
704 | 120 | """A mock keyring that stores keys according to a given attr.""" | ||
705 | 121 | |||
706 | 122 | def __init__(self): | ||
707 | 123 | """Initialize this instance.""" | ||
708 | 124 | self.id_counter = itertools.count() | ||
709 | 125 | self.store = {} | ||
710 | 126 | self.deleted = [] | ||
711 | 127 | |||
712 | 128 | def _get_next_id(self): | ||
713 | 129 | """Return the next keyring id.""" | ||
714 | 130 | return self.id_counter.next() | ||
715 | 131 | |||
716 | 132 | def item_create_sync(self, keyring_name, item_type, key_name, attr, | ||
717 | 133 | secret, update_if_exists): | ||
718 | 134 | """Add a key to a keyring.""" | ||
719 | 135 | new_id = self._get_next_id() | ||
720 | 136 | i = MockKeyringItem(new_id, keyring_name, attr, secret) | ||
721 | 137 | self.store[new_id] = i | ||
722 | 138 | |||
723 | 139 | def item_delete_sync(self, keyring_name, item_id): | ||
724 | 140 | """Delete a key from a keyring, and keep a list of deleted keys.""" | ||
725 | 141 | item = self.store.pop(item_id) | ||
726 | 142 | assert keyring_name == item.keyring | ||
727 | 143 | self.deleted.append(item) | ||
728 | 144 | |||
729 | 145 | def find_items_sync(self, item_type, attr): | ||
730 | 146 | """Find all keys that match the given attributes.""" | ||
731 | 147 | items = [i for i in self.store.values() if i.matches(attr)] | ||
732 | 148 | if len(items) == 0: | ||
733 | 149 | raise gnomekeyring.NoMatchError() | ||
734 | 150 | return items | ||
735 | 151 | |||
736 | 152 | def is_available(self): | ||
737 | 153 | """A very available keyring.""" | ||
738 | 154 | return True | ||
739 | 155 | >>>>>>> MERGE-SOURCE | ||
740 | 117 | 156 | ||
741 | 118 | 157 | ||
742 | 119 | class TestTokenNameBuilder(TestCase): | 158 | class TestTokenNameBuilder(TestCase): |
743 | @@ -160,9 +199,17 @@ | |||
744 | 160 | 199 | ||
745 | 161 | def setUp(self): | 200 | def setUp(self): |
746 | 162 | """Initialize the mock used in these tests.""" | 201 | """Initialize the mock used in these tests.""" |
747 | 202 | <<<<<<< TREE | ||
748 | 163 | self.mock_service = None | 203 | self.mock_service = None |
749 | 164 | self.service = self.patch(keyring, "SecretService", | 204 | self.service = self.patch(keyring, "SecretService", |
750 | 165 | self.get_mock_service) | 205 | self.get_mock_service) |
751 | 206 | ======= | ||
752 | 207 | self.mgk = MockGnomeKeyring() | ||
753 | 208 | self.patch(gnomekeyring, "item_create_sync", self.mgk.item_create_sync) | ||
754 | 209 | self.patch(gnomekeyring, "is_available", self.mgk.is_available) | ||
755 | 210 | self.patch(gnomekeyring, "find_items_sync", self.mgk.find_items_sync) | ||
756 | 211 | self.patch(gnomekeyring, "item_delete_sync", self.mgk.item_delete_sync) | ||
757 | 212 | >>>>>>> MERGE-SOURCE | ||
758 | 166 | fake_gethostname = build_fake_gethostname("darkstar") | 213 | fake_gethostname = build_fake_gethostname("darkstar") |
759 | 167 | self.patch(socket, "gethostname", fake_gethostname) | 214 | self.patch(socket, "gethostname", fake_gethostname) |
760 | 168 | 215 | ||
761 | @@ -177,6 +224,7 @@ | |||
762 | 177 | """Test that the set method does not erase previous keys.""" | 224 | """Test that the set method does not erase previous keys.""" |
763 | 178 | sample_creds = {"name": "sample creds name"} | 225 | sample_creds = {"name": "sample creds name"} |
764 | 179 | sample_creds2 = {"name": "sample creds name 2"} | 226 | sample_creds2 = {"name": "sample creds name 2"} |
765 | 227 | <<<<<<< TREE | ||
766 | 180 | kr = keyring.Keyring() | 228 | kr = keyring.Keyring() |
767 | 181 | yield kr.set_credentials("appname", sample_creds) | 229 | yield kr.set_credentials("appname", sample_creds) |
768 | 182 | yield kr.set_credentials("appname", sample_creds2) | 230 | yield kr.set_credentials("appname", sample_creds2) |
769 | @@ -186,6 +234,15 @@ | |||
770 | 186 | 234 | ||
771 | 187 | @inlineCallbacks | 235 | @inlineCallbacks |
772 | 188 | def test_delete_credentials(self): | 236 | def test_delete_credentials(self): |
773 | 237 | ======= | ||
774 | 238 | keyring.Keyring("appname").set_ubuntusso_attr(sample_creds) | ||
775 | 239 | keyring.Keyring("appname").set_ubuntusso_attr(sample_creds2) | ||
776 | 240 | |||
777 | 241 | self.assertEqual(len(self.mgk.store), 2) | ||
778 | 242 | self.assertEqual(len(self.mgk.deleted), 0) | ||
779 | 243 | |||
780 | 244 | def test_delete_ubuntusso(self): | ||
781 | 245 | >>>>>>> MERGE-SOURCE | ||
782 | 189 | """Test that a given key is deleted.""" | 246 | """Test that a given key is deleted.""" |
783 | 190 | sample_creds = {"name": "sample creds name"} | 247 | sample_creds = {"name": "sample creds name"} |
784 | 191 | kr = keyring.Keyring() | 248 | kr = keyring.Keyring() |
785 | @@ -225,11 +282,22 @@ | |||
786 | 225 | "oauth_token": sample_oauth_token, | 282 | "oauth_token": sample_oauth_token, |
787 | 226 | "oauth_token_secret": sample_oauth_secret, | 283 | "oauth_token_secret": sample_oauth_secret, |
788 | 227 | } | 284 | } |
789 | 285 | <<<<<<< TREE | ||
790 | 228 | u1kr = common_keyring.UbuntuOneOAuthKeyring() | 286 | u1kr = common_keyring.UbuntuOneOAuthKeyring() |
791 | 229 | yield u1kr.set_credentials(keyring.U1_APP_NAME, old_creds) | 287 | yield u1kr.set_credentials(keyring.U1_APP_NAME, old_creds) |
792 | 230 | 288 | ||
793 | 231 | kr = keyring.Keyring() | 289 | kr = keyring.Keyring() |
794 | 232 | result = yield kr.get_credentials(keyring.U1_APP_NAME) | 290 | result = yield kr.get_credentials(keyring.U1_APP_NAME) |
795 | 291 | ======= | ||
796 | 292 | secret = urllib.urlencode(old_creds) | ||
797 | 293 | self.mgk.item_create_sync(None, None, | ||
798 | 294 | keyring.U1_APP_NAME, | ||
799 | 295 | keyring.U1_KEY_ATTR, | ||
800 | 296 | secret, True) | ||
801 | 297 | |||
802 | 298 | result = keyring.Keyring(keyring.U1_APP_NAME).get_ubuntusso_attr() | ||
803 | 299 | |||
804 | 300 | >>>>>>> MERGE-SOURCE | ||
805 | 233 | self.assertIn("token", result) | 301 | self.assertIn("token", result) |
806 | 234 | self.assertEqual(result["token"], sample_oauth_token) | 302 | self.assertEqual(result["token"], sample_oauth_token) |
807 | 235 | self.assertIn("token_secret", result) | 303 | self.assertIn("token_secret", result) |
808 | @@ -244,8 +312,16 @@ | |||
809 | 244 | "oauth_token": sample_oauth_token, | 312 | "oauth_token": sample_oauth_token, |
810 | 245 | "oauth_token_secret": sample_oauth_secret, | 313 | "oauth_token_secret": sample_oauth_secret, |
811 | 246 | } | 314 | } |
812 | 315 | <<<<<<< TREE | ||
813 | 247 | u1kr = common_keyring.UbuntuOneOAuthKeyring() | 316 | u1kr = common_keyring.UbuntuOneOAuthKeyring() |
814 | 248 | yield u1kr.set_credentials(keyring.U1_APP_NAME, old_creds) | 317 | yield u1kr.set_credentials(keyring.U1_APP_NAME, old_creds) |
815 | 318 | ======= | ||
816 | 319 | secret = urllib.urlencode(old_creds) | ||
817 | 320 | self.mgk.item_create_sync(None, None, | ||
818 | 321 | keyring.U1_APP_NAME, | ||
819 | 322 | keyring.U1_KEY_ATTR, | ||
820 | 323 | secret, True) | ||
821 | 324 | >>>>>>> MERGE-SOURCE | ||
822 | 249 | 325 | ||
823 | 250 | kr = keyring.Keyring() | 326 | kr = keyring.Keyring() |
824 | 251 | result = yield kr.get_credentials("Software Center") | 327 | result = yield kr.get_credentials("Software Center") |
825 | 252 | 328 | ||
826 | === modified file 'ubuntu_sso/main/linux.py' | |||
827 | --- ubuntu_sso/main/linux.py 2011-03-29 14:16:36 +0000 | |||
828 | +++ ubuntu_sso/main/linux.py 2011-03-29 17:35:28 +0000 | |||
829 | @@ -45,6 +45,279 @@ | |||
830 | 45 | 45 | ||
831 | 46 | 46 | ||
832 | 47 | logger = setup_logging("ubuntu_sso.main") | 47 | logger = setup_logging("ubuntu_sso.main") |
833 | 48 | <<<<<<< TREE | ||
834 | 49 | ======= | ||
835 | 50 | PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/" | ||
836 | 51 | SERVICE_URL = "https://login.ubuntu.com/api/1.0" | ||
837 | 52 | NO_OP = lambda *args, **kwargs: None | ||
838 | 53 | |||
839 | 54 | |||
840 | 55 | class NoDefaultConfigError(Exception): | ||
841 | 56 | """No default section in configuration file""" | ||
842 | 57 | |||
843 | 58 | |||
844 | 59 | class BadRealmError(Exception): | ||
845 | 60 | """Realm must be a URL.""" | ||
846 | 61 | |||
847 | 62 | |||
848 | 63 | class InvalidEmailError(Exception): | ||
849 | 64 | """The email is not valid.""" | ||
850 | 65 | |||
851 | 66 | |||
852 | 67 | class InvalidPasswordError(Exception): | ||
853 | 68 | """The password is not valid. | ||
854 | 69 | |||
855 | 70 | Must provide at least 8 characters, one upper case, one number. | ||
856 | 71 | """ | ||
857 | 72 | |||
858 | 73 | |||
859 | 74 | class RegistrationError(Exception): | ||
860 | 75 | """The registration failed.""" | ||
861 | 76 | |||
862 | 77 | |||
863 | 78 | class AuthenticationError(Exception): | ||
864 | 79 | """The authentication failed.""" | ||
865 | 80 | |||
866 | 81 | |||
867 | 82 | class EmailTokenError(Exception): | ||
868 | 83 | """The email token is not valid.""" | ||
869 | 84 | |||
870 | 85 | |||
871 | 86 | class ResetPasswordTokenError(Exception): | ||
872 | 87 | """The token for password reset could not be generated.""" | ||
873 | 88 | |||
874 | 89 | |||
875 | 90 | class NewPasswordError(Exception): | ||
876 | 91 | """The new password could not be set.""" | ||
877 | 92 | |||
878 | 93 | |||
879 | 94 | def keyring_store_credentials(app_name, credentials, callback, *cb_args): | ||
880 | 95 | """Store the credentials in the keyring.""" | ||
881 | 96 | |||
882 | 97 | def _inner(): | ||
883 | 98 | """Store the credentials, and trigger the callback.""" | ||
884 | 99 | logger.info('keyring_store_credentials: app_name "%s".', app_name) | ||
885 | 100 | Keyring(app_name).set_ubuntusso_attr(credentials) | ||
886 | 101 | callback(*cb_args) | ||
887 | 102 | |||
888 | 103 | gobject.idle_add(_inner) | ||
889 | 104 | |||
890 | 105 | |||
891 | 106 | def keyring_get_credentials(app_name): | ||
892 | 107 | """Get the credentials from the keyring or None if not there.""" | ||
893 | 108 | creds = Keyring(app_name).get_ubuntusso_attr() | ||
894 | 109 | logger.info('keyring_get_credentials: app_name "%s", resulting credentials' | ||
895 | 110 | ' is not None? %r', app_name, creds is not None) | ||
896 | 111 | return creds | ||
897 | 112 | |||
898 | 113 | |||
899 | 114 | class SSOLoginProcessor(object): | ||
900 | 115 | """Login and register users using the Ubuntu Single Sign On service.""" | ||
901 | 116 | |||
902 | 117 | def __init__(self, sso_service_class=None): | ||
903 | 118 | """Create a new SSO login processor.""" | ||
904 | 119 | if sso_service_class is None: | ||
905 | 120 | self.sso_service_class = ServiceRoot | ||
906 | 121 | else: | ||
907 | 122 | self.sso_service_class = sso_service_class | ||
908 | 123 | |||
909 | 124 | self.service_url = os.environ.get('USSOC_SERVICE_URL', SERVICE_URL) | ||
910 | 125 | |||
911 | 126 | def _valid_email(self, email): | ||
912 | 127 | """Validate the given email.""" | ||
913 | 128 | return email is not None and '@' in email | ||
914 | 129 | |||
915 | 130 | def _valid_password(self, password): | ||
916 | 131 | """Validate the given password.""" | ||
917 | 132 | res = (len(password) > 7 and # at least 8 characters | ||
918 | 133 | re.search('[A-Z]', password) and # one upper case | ||
919 | 134 | re.search('\d+', password)) # one number | ||
920 | 135 | return res | ||
921 | 136 | |||
922 | 137 | def _format_webservice_errors(self, errdict): | ||
923 | 138 | """Turn each list of strings in the errdict into a LF separated str.""" | ||
924 | 139 | result = {} | ||
925 | 140 | for k, v in errdict.iteritems(): | ||
926 | 141 | # workaround until bug #624955 is solved | ||
927 | 142 | if isinstance(v, basestring): | ||
928 | 143 | result[k] = v | ||
929 | 144 | else: | ||
930 | 145 | result[k] = "\n".join(v) | ||
931 | 146 | return result | ||
932 | 147 | |||
933 | 148 | def generate_captcha(self, filename): | ||
934 | 149 | """Generate a captcha using the SSO service.""" | ||
935 | 150 | logger.debug('generate_captcha: requesting captcha, filename: %r', | ||
936 | 151 | filename) | ||
937 | 152 | sso_service = self.sso_service_class(None, self.service_url) | ||
938 | 153 | captcha = sso_service.captchas.new() | ||
939 | 154 | |||
940 | 155 | # download captcha and save to 'filename' | ||
941 | 156 | logger.debug('generate_captcha: server answered: %r', captcha) | ||
942 | 157 | try: | ||
943 | 158 | res = urllib2.urlopen(captcha['image_url']) | ||
944 | 159 | with open(filename, 'wb') as f: | ||
945 | 160 | f.write(res.read()) | ||
946 | 161 | except: | ||
947 | 162 | msg = 'generate_captcha crashed while downloading the image.' | ||
948 | 163 | logger.exception(msg) | ||
949 | 164 | raise | ||
950 | 165 | |||
951 | 166 | return captcha['captcha_id'] | ||
952 | 167 | |||
953 | 168 | def register_with_name(self, email, password, displayname, | ||
954 | 169 | captcha_id, captcha_solution): | ||
955 | 170 | """Register a new user with 'email' and 'password'.""" | ||
956 | 171 | logger.debug('register_with_name: email: %r password: <hidden>, ' | ||
957 | 172 | 'displayname: %r, captcha_id: %r, captcha_solution: %r', | ||
958 | 173 | email, displayname, captcha_id, captcha_solution) | ||
959 | 174 | sso_service = self.sso_service_class(None, self.service_url) | ||
960 | 175 | if not self._valid_email(email): | ||
961 | 176 | logger.error('register_with_name: InvalidEmailError for email: %r', | ||
962 | 177 | email) | ||
963 | 178 | raise InvalidEmailError() | ||
964 | 179 | if not self._valid_password(password): | ||
965 | 180 | logger.error('register_with_name: InvalidPasswordError') | ||
966 | 181 | raise InvalidPasswordError() | ||
967 | 182 | |||
968 | 183 | result = sso_service.registrations.register( | ||
969 | 184 | email=email, password=password, | ||
970 | 185 | displayname=displayname, | ||
971 | 186 | captcha_id=captcha_id, | ||
972 | 187 | captcha_solution=captcha_solution) | ||
973 | 188 | logger.info('register_with_name: email: %r result: %r', email, result) | ||
974 | 189 | |||
975 | 190 | if result['status'] == 'error': | ||
976 | 191 | errorsdict = self._format_webservice_errors(result['errors']) | ||
977 | 192 | raise RegistrationError(errorsdict) | ||
978 | 193 | elif result['status'] != 'ok': | ||
979 | 194 | raise RegistrationError('Received unknown status: %s' % result) | ||
980 | 195 | else: | ||
981 | 196 | return email | ||
982 | 197 | |||
983 | 198 | def register_user(self, email, password, | ||
984 | 199 | captcha_id, captcha_solution): | ||
985 | 200 | """Register a new user with 'email' and 'password'.""" | ||
986 | 201 | logger.debug('register_user: email: %r password: <hidden>, ' | ||
987 | 202 | 'captcha_id: %r, captcha_solution: %r', | ||
988 | 203 | email, captcha_id, captcha_solution) | ||
989 | 204 | res = self.register_with_name(email, password, displayname='', | ||
990 | 205 | captcha_id=captcha_id, | ||
991 | 206 | captcha_solution=captcha_solution) | ||
992 | 207 | return res | ||
993 | 208 | |||
994 | 209 | def login(self, email, password, token_name): | ||
995 | 210 | """Login a user with 'email' and 'password'.""" | ||
996 | 211 | logger.debug('login: email: %r password: <hidden>, token_name: %r', | ||
997 | 212 | email, token_name) | ||
998 | 213 | basic = BasicHttpAuthorizer(email, password) | ||
999 | 214 | sso_service = self.sso_service_class(basic, self.service_url) | ||
1000 | 215 | service = sso_service.authentications.authenticate | ||
1001 | 216 | |||
1002 | 217 | try: | ||
1003 | 218 | credentials = service(token_name=token_name) | ||
1004 | 219 | except HTTPError: | ||
1005 | 220 | logger.exception('login failed with:') | ||
1006 | 221 | raise AuthenticationError() | ||
1007 | 222 | |||
1008 | 223 | logger.debug('login: authentication successful! consumer_key: %r, ' \ | ||
1009 | 224 | 'token_name: %r', credentials['consumer_key'], token_name) | ||
1010 | 225 | return credentials | ||
1011 | 226 | |||
1012 | 227 | def is_validated(self, token, sso_service=None): | ||
1013 | 228 | """Return if user with 'email' and 'password' is validated.""" | ||
1014 | 229 | logger.debug('is_validated: requesting accounts.me() info.') | ||
1015 | 230 | if sso_service is None: | ||
1016 | 231 | oauth_token = oauth.OAuthToken(token['token'], | ||
1017 | 232 | token['token_secret']) | ||
1018 | 233 | authorizer = OAuthAuthorizer(token['consumer_key'], | ||
1019 | 234 | token['consumer_secret'], | ||
1020 | 235 | oauth_token) | ||
1021 | 236 | sso_service = self.sso_service_class(authorizer, self.service_url) | ||
1022 | 237 | |||
1023 | 238 | me_info = sso_service.accounts.me() | ||
1024 | 239 | key = 'preferred_email' | ||
1025 | 240 | result = key in me_info and me_info[key] != None | ||
1026 | 241 | |||
1027 | 242 | logger.info('is_validated: consumer_key: %r, result: %r.', | ||
1028 | 243 | token['consumer_key'], result) | ||
1029 | 244 | return result | ||
1030 | 245 | |||
1031 | 246 | def validate_email(self, email, password, email_token, token_name): | ||
1032 | 247 | """Validate an email token for user with 'email' and 'password'.""" | ||
1033 | 248 | logger.debug('validate_email: email: %r password: <hidden>, ' | ||
1034 | 249 | 'email_token: %r, token_name: %r.', | ||
1035 | 250 | email, email_token, token_name) | ||
1036 | 251 | token = self.login(email=email, password=password, | ||
1037 | 252 | token_name=token_name) | ||
1038 | 253 | |||
1039 | 254 | oauth_token = oauth.OAuthToken(token['token'], token['token_secret']) | ||
1040 | 255 | authorizer = OAuthAuthorizer(token['consumer_key'], | ||
1041 | 256 | token['consumer_secret'], | ||
1042 | 257 | oauth_token) | ||
1043 | 258 | sso_service = self.sso_service_class(authorizer, self.service_url) | ||
1044 | 259 | result = sso_service.accounts.validate_email(email_token=email_token) | ||
1045 | 260 | logger.info('validate_email: email: %r result: %r', email, result) | ||
1046 | 261 | if 'errors' in result: | ||
1047 | 262 | errorsdict = self._format_webservice_errors(result['errors']) | ||
1048 | 263 | raise EmailTokenError(errorsdict) | ||
1049 | 264 | elif 'email' in result: | ||
1050 | 265 | return token | ||
1051 | 266 | else: | ||
1052 | 267 | raise EmailTokenError('Received invalid reply: %s' % result) | ||
1053 | 268 | |||
1054 | 269 | def request_password_reset_token(self, email): | ||
1055 | 270 | """Request a token to reset the password for the account 'email'.""" | ||
1056 | 271 | sso_service = self.sso_service_class(None, self.service_url) | ||
1057 | 272 | service = sso_service.registrations.request_password_reset_token | ||
1058 | 273 | try: | ||
1059 | 274 | result = service(email=email) | ||
1060 | 275 | except HTTPError, e: | ||
1061 | 276 | logger.exception('request_password_reset_token failed with:') | ||
1062 | 277 | raise ResetPasswordTokenError(e.content.split('\n')[0]) | ||
1063 | 278 | |||
1064 | 279 | if result['status'] == 'ok': | ||
1065 | 280 | return email | ||
1066 | 281 | else: | ||
1067 | 282 | raise ResetPasswordTokenError('Received invalid reply: %s' % | ||
1068 | 283 | result) | ||
1069 | 284 | |||
1070 | 285 | def set_new_password(self, email, token, new_password): | ||
1071 | 286 | """Set a new password for the account 'email' to be 'new_password'. | ||
1072 | 287 | |||
1073 | 288 | The 'token' has to be the one resulting from a call to | ||
1074 | 289 | 'request_password_reset_token'. | ||
1075 | 290 | |||
1076 | 291 | """ | ||
1077 | 292 | sso_service = self.sso_service_class(None, self.service_url) | ||
1078 | 293 | service = sso_service.registrations.set_new_password | ||
1079 | 294 | try: | ||
1080 | 295 | result = service(email=email, token=token, | ||
1081 | 296 | new_password=new_password) | ||
1082 | 297 | except HTTPError, e: | ||
1083 | 298 | logger.exception('set_new_password failed with:') | ||
1084 | 299 | raise NewPasswordError(e.content.split('\n')[0]) | ||
1085 | 300 | |||
1086 | 301 | if result['status'] == 'ok': | ||
1087 | 302 | return email | ||
1088 | 303 | else: | ||
1089 | 304 | raise NewPasswordError('Received invalid reply: %s' % result) | ||
1090 | 305 | |||
1091 | 306 | |||
1092 | 307 | def except_to_errdict(e): | ||
1093 | 308 | """Turn an exception into a dictionary to return thru DBus.""" | ||
1094 | 309 | result = { | ||
1095 | 310 | "errtype": e.__class__.__name__, | ||
1096 | 311 | } | ||
1097 | 312 | if len(e.args) == 0: | ||
1098 | 313 | result["message"] = e.__class__.__doc__ | ||
1099 | 314 | elif isinstance(e.args[0], dict): | ||
1100 | 315 | result.update(e.args[0]) | ||
1101 | 316 | elif isinstance(e.args[0], basestring): | ||
1102 | 317 | result["message"] = e.args[0] | ||
1103 | 318 | |||
1104 | 319 | return result | ||
1105 | 320 | >>>>>>> MERGE-SOURCE | ||
1106 | 48 | 321 | ||
1107 | 49 | 322 | ||
1108 | 50 | def blocking(f, app_name, result_cb, error_cb): | 323 | def blocking(f, app_name, result_cb, error_cb): |
1109 | @@ -72,7 +345,13 @@ | |||
1110 | 72 | """Initiate the Login object.""" | 345 | """Initiate the Login object.""" |
1111 | 73 | dbus.service.Object.__init__(self, object_path=object_path, | 346 | dbus.service.Object.__init__(self, object_path=object_path, |
1112 | 74 | bus_name=bus_name) | 347 | bus_name=bus_name) |
1113 | 348 | <<<<<<< TREE | ||
1114 | 75 | self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class) | 349 | self.root = SSOLoginRoot(sso_login_processor_class, sso_service_class) |
1115 | 350 | ======= | ||
1116 | 351 | self.sso_login_processor_class = sso_login_processor_class | ||
1117 | 352 | self.processor = self.sso_login_processor_class( | ||
1118 | 353 | sso_service_class=sso_service_class) | ||
1119 | 354 | >>>>>>> MERGE-SOURCE | ||
1120 | 76 | 355 | ||
1121 | 77 | # generate_capcha signals | 356 | # generate_capcha signals |
1122 | 78 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 357 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1123 | @@ -91,9 +370,17 @@ | |||
1124 | 91 | in_signature='ss') | 370 | in_signature='ss') |
1125 | 92 | def generate_captcha(self, app_name, filename): | 371 | def generate_captcha(self, app_name, filename): |
1126 | 93 | """Call the matching method in the processor.""" | 372 | """Call the matching method in the processor.""" |
1127 | 373 | <<<<<<< TREE | ||
1128 | 94 | self.root.generate_captcha(app_name, filename, blocking, | 374 | self.root.generate_captcha(app_name, filename, blocking, |
1129 | 95 | self.CaptchaGenerated, | 375 | self.CaptchaGenerated, |
1130 | 96 | self.CaptchaGenerationError) | 376 | self.CaptchaGenerationError) |
1131 | 377 | ======= | ||
1132 | 378 | def f(): | ||
1133 | 379 | """Inner function that will be run in a thread.""" | ||
1134 | 380 | return self.processor.generate_captcha(filename) | ||
1135 | 381 | blocking(f, app_name, self.CaptchaGenerated, | ||
1136 | 382 | self.CaptchaGenerationError) | ||
1137 | 383 | >>>>>>> MERGE-SOURCE | ||
1138 | 97 | 384 | ||
1139 | 98 | # register_user signals | 385 | # register_user signals |
1140 | 99 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 386 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1141 | @@ -113,10 +400,29 @@ | |||
1142 | 113 | def register_user(self, app_name, email, password, name, | 400 | def register_user(self, app_name, email, password, name, |
1143 | 114 | captcha_id, captcha_solution): | 401 | captcha_id, captcha_solution): |
1144 | 115 | """Call the matching method in the processor.""" | 402 | """Call the matching method in the processor.""" |
1145 | 403 | <<<<<<< TREE | ||
1146 | 116 | self.root.register_user(app_name, email, password, name, captcha_id, | 404 | self.root.register_user(app_name, email, password, name, captcha_id, |
1147 | 117 | captcha_solution, blocking, | 405 | captcha_solution, blocking, |
1148 | 118 | self.UserRegistered, | 406 | self.UserRegistered, |
1149 | 119 | self.UserRegistrationError) | 407 | self.UserRegistrationError) |
1150 | 408 | ======= | ||
1151 | 409 | def f(): | ||
1152 | 410 | """Inner function that will be run in a thread.""" | ||
1153 | 411 | return self.processor.register_user(email, password, | ||
1154 | 412 | captcha_id, captcha_solution) | ||
1155 | 413 | blocking(f, app_name, self.UserRegistered, self.UserRegistrationError) | ||
1156 | 414 | |||
1157 | 415 | @dbus.service.method(dbus_interface=DBUS_IFACE_USER_NAME, | ||
1158 | 416 | in_signature='ssssss') | ||
1159 | 417 | def register_with_name(self, app_name, email, password, name, | ||
1160 | 418 | captcha_id, captcha_solution): | ||
1161 | 419 | """Call the matching method in the processor.""" | ||
1162 | 420 | def f(): | ||
1163 | 421 | """Inner function that will be run in a thread.""" | ||
1164 | 422 | return self.processor.register_with_name(email, password, name, | ||
1165 | 423 | captcha_id, captcha_solution) | ||
1166 | 424 | blocking(f, app_name, self.UserRegistered, self.UserRegistrationError) | ||
1167 | 425 | >>>>>>> MERGE-SOURCE | ||
1168 | 120 | 426 | ||
1169 | 121 | # login signals | 427 | # login signals |
1170 | 122 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 428 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1171 | @@ -141,8 +447,31 @@ | |||
1172 | 141 | in_signature='sss') | 447 | in_signature='sss') |
1173 | 142 | def login(self, app_name, email, password): | 448 | def login(self, app_name, email, password): |
1174 | 143 | """Call the matching method in the processor.""" | 449 | """Call the matching method in the processor.""" |
1175 | 450 | <<<<<<< TREE | ||
1176 | 144 | self.root.login(app_name, email, password, blocking, self.LoggedIn, | 451 | self.root.login(app_name, email, password, blocking, self.LoggedIn, |
1177 | 145 | self.LoginError, self.UserNotValidated) | 452 | self.LoginError, self.UserNotValidated) |
1178 | 453 | ======= | ||
1179 | 454 | def f(): | ||
1180 | 455 | """Inner function that will be run in a thread.""" | ||
1181 | 456 | token_name = get_token_name(app_name) | ||
1182 | 457 | logger.debug('login: token_name %r, email %r, password <hidden>.', | ||
1183 | 458 | token_name, email) | ||
1184 | 459 | credentials = self.processor.login(email, password, token_name) | ||
1185 | 460 | logger.debug('login returned not None credentials? %r.', | ||
1186 | 461 | credentials is not None) | ||
1187 | 462 | return credentials | ||
1188 | 463 | |||
1189 | 464 | def success_cb(app_name, credentials): | ||
1190 | 465 | """Login finished successfull.""" | ||
1191 | 466 | is_validated = self.processor.is_validated(credentials) | ||
1192 | 467 | logger.debug('user is validated? %r.', is_validated) | ||
1193 | 468 | if is_validated: | ||
1194 | 469 | keyring_store_credentials(app_name, credentials, | ||
1195 | 470 | self.LoggedIn, app_name, email) | ||
1196 | 471 | else: | ||
1197 | 472 | self.UserNotValidated(app_name, email) | ||
1198 | 473 | blocking(f, app_name, success_cb, self.LoginError) | ||
1199 | 474 | >>>>>>> MERGE-SOURCE | ||
1200 | 146 | 475 | ||
1201 | 147 | # validate_email signals | 476 | # validate_email signals |
1202 | 148 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 477 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1203 | @@ -161,9 +490,25 @@ | |||
1204 | 161 | in_signature='ssss') | 490 | in_signature='ssss') |
1205 | 162 | def validate_email(self, app_name, email, password, email_token): | 491 | def validate_email(self, app_name, email, password, email_token): |
1206 | 163 | """Call the matching method in the processor.""" | 492 | """Call the matching method in the processor.""" |
1207 | 493 | <<<<<<< TREE | ||
1208 | 164 | self.root.validate_email(app_name, email, password, email_token, | 494 | self.root.validate_email(app_name, email, password, email_token, |
1209 | 165 | blocking, self.EmailValidated, | 495 | blocking, self.EmailValidated, |
1210 | 166 | self.EmailValidationError) | 496 | self.EmailValidationError) |
1211 | 497 | ======= | ||
1212 | 498 | def f(): | ||
1213 | 499 | """Inner function that will be run in a thread.""" | ||
1214 | 500 | token_name = get_token_name(app_name) | ||
1215 | 501 | credentials = self.processor.validate_email(email, password, | ||
1216 | 502 | email_token, token_name) | ||
1217 | 503 | |||
1218 | 504 | def _email_stored(): | ||
1219 | 505 | """The email was stored, so call the signal.""" | ||
1220 | 506 | self.EmailValidated(app_name, email) | ||
1221 | 507 | |||
1222 | 508 | keyring_store_credentials(app_name, credentials, _email_stored) | ||
1223 | 509 | |||
1224 | 510 | blocking(f, app_name, NO_OP, self.EmailValidationError) | ||
1225 | 511 | >>>>>>> MERGE-SOURCE | ||
1226 | 167 | 512 | ||
1227 | 168 | # request_password_reset_token signals | 513 | # request_password_reset_token signals |
1228 | 169 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 514 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1229 | @@ -182,9 +527,17 @@ | |||
1230 | 182 | in_signature='ss') | 527 | in_signature='ss') |
1231 | 183 | def request_password_reset_token(self, app_name, email): | 528 | def request_password_reset_token(self, app_name, email): |
1232 | 184 | """Call the matching method in the processor.""" | 529 | """Call the matching method in the processor.""" |
1233 | 530 | <<<<<<< TREE | ||
1234 | 185 | self.root.request_password_reset_token(app_name, email, blocking, | 531 | self.root.request_password_reset_token(app_name, email, blocking, |
1235 | 186 | self.PasswordResetTokenSent, | 532 | self.PasswordResetTokenSent, |
1236 | 187 | self.PasswordResetError) | 533 | self.PasswordResetError) |
1237 | 534 | ======= | ||
1238 | 535 | def f(): | ||
1239 | 536 | """Inner function that will be run in a thread.""" | ||
1240 | 537 | return self.processor.request_password_reset_token(email) | ||
1241 | 538 | blocking(f, app_name, self.PasswordResetTokenSent, | ||
1242 | 539 | self.PasswordResetError) | ||
1243 | 540 | >>>>>>> MERGE-SOURCE | ||
1244 | 188 | 541 | ||
1245 | 189 | # set_new_password signals | 542 | # set_new_password signals |
1246 | 190 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") | 543 | @dbus.service.signal(DBUS_IFACE_USER_NAME, signature="ss") |
1247 | @@ -203,9 +556,17 @@ | |||
1248 | 203 | in_signature='ssss') | 556 | in_signature='ssss') |
1249 | 204 | def set_new_password(self, app_name, email, token, new_password): | 557 | def set_new_password(self, app_name, email, token, new_password): |
1250 | 205 | """Call the matching method in the processor.""" | 558 | """Call the matching method in the processor.""" |
1251 | 559 | <<<<<<< TREE | ||
1252 | 206 | self.root.set_new_password(app_name, email, token, new_password, | 560 | self.root.set_new_password(app_name, email, token, new_password, |
1253 | 207 | blocking, self.PasswordChanged, | 561 | blocking, self.PasswordChanged, |
1254 | 208 | self.PasswordChangeError) | 562 | self.PasswordChangeError) |
1255 | 563 | ======= | ||
1256 | 564 | def f(): | ||
1257 | 565 | """Inner function that will be run in a thread.""" | ||
1258 | 566 | return self.processor.set_new_password(email, token, | ||
1259 | 567 | new_password) | ||
1260 | 568 | blocking(f, app_name, self.PasswordChanged, self.PasswordChangeError) | ||
1261 | 569 | >>>>>>> MERGE-SOURCE | ||
1262 | 209 | 570 | ||
1263 | 210 | 571 | ||
1264 | 211 | class SSOCredentials(dbus.service.Object): | 572 | class SSOCredentials(dbus.service.Object): |
1265 | @@ -243,11 +604,92 @@ | |||
1266 | 243 | '"%s" and error_message %r', app_name, error_message) | 604 | '"%s" and error_message %r', app_name, error_message) |
1267 | 244 | 605 | ||
1268 | 245 | @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME, | 606 | @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME, |
1269 | 607 | <<<<<<< TREE | ||
1270 | 246 | in_signature="s", out_signature="a{ss}", | 608 | in_signature="s", out_signature="a{ss}", |
1271 | 247 | async_callbacks=("callback", "errback")) | 609 | async_callbacks=("callback", "errback")) |
1272 | 248 | def find_credentials(self, app_name, callback=NO_OP, errback=NO_OP): | 610 | def find_credentials(self, app_name, callback=NO_OP, errback=NO_OP): |
1273 | 249 | """Get the credentials from the keyring or {} if not there.""" | 611 | """Get the credentials from the keyring or {} if not there.""" |
1274 | 250 | self.root.find_credentials(app_name, callback, errback) | 612 | self.root.find_credentials(app_name, callback, errback) |
1275 | 613 | ======= | ||
1276 | 614 | in_signature="s", out_signature="a{ss}") | ||
1277 | 615 | def find_credentials(self, app_name): | ||
1278 | 616 | """Get the credentials from the keyring or '' if not there.""" | ||
1279 | 617 | token = keyring_get_credentials(app_name) | ||
1280 | 618 | logger.info('find_credentials: app_name "%s", result is {}? %s', | ||
1281 | 619 | app_name, token is None) | ||
1282 | 620 | if token is None: | ||
1283 | 621 | return {} | ||
1284 | 622 | else: | ||
1285 | 623 | return token | ||
1286 | 624 | |||
1287 | 625 | def _login_success_cb(self, dialog, app_name, email): | ||
1288 | 626 | """Handles the response from the UI dialog.""" | ||
1289 | 627 | logger.info('Login successful for app %r, email %r. Still pending to ' | ||
1290 | 628 | 'ping server and send result signal.', app_name, email) | ||
1291 | 629 | try: | ||
1292 | 630 | creds = keyring_get_credentials(app_name) | ||
1293 | 631 | self._ping_url(app_name, email, creds) | ||
1294 | 632 | self.CredentialsFound(app_name, creds) | ||
1295 | 633 | except: # pylint: disable=W0702 | ||
1296 | 634 | msg = "Problem getting the credentials from the keyring." | ||
1297 | 635 | logger.exception(msg) | ||
1298 | 636 | self.clear_token(app_name) | ||
1299 | 637 | self.CredentialsError(app_name, msg, traceback.format_exc()) | ||
1300 | 638 | |||
1301 | 639 | def _login_error_cb(self, dialog, app_name, error): | ||
1302 | 640 | """Handles a problem in the UI.""" | ||
1303 | 641 | logger.info('Login unsuccessful for app %r, error %r', app_name, error) | ||
1304 | 642 | msg = "Problem getting the credentials from the keyring." | ||
1305 | 643 | self.CredentialsError(app_name, msg, "no more info available") | ||
1306 | 644 | |||
1307 | 645 | def _login_auth_denied_cb(self, dialog, app_name): | ||
1308 | 646 | """The user decides not to allow the registration or login.""" | ||
1309 | 647 | self.AuthorizationDenied(app_name) | ||
1310 | 648 | |||
1311 | 649 | def _ping_url(self, app_name, email, credentials): | ||
1312 | 650 | """Ping the given url.""" | ||
1313 | 651 | logger.info('Maybe pinging server for app_name "%s"', app_name) | ||
1314 | 652 | if app_name == U1_APP_NAME: | ||
1315 | 653 | url = self.ping_url + email | ||
1316 | 654 | consumer = oauth.OAuthConsumer(credentials['consumer_key'], | ||
1317 | 655 | credentials['consumer_secret']) | ||
1318 | 656 | token = oauth.OAuthToken(credentials['token'], | ||
1319 | 657 | credentials['token_secret']) | ||
1320 | 658 | get_request = oauth.OAuthRequest.from_consumer_and_token | ||
1321 | 659 | oauth_req = get_request(oauth_consumer=consumer, token=token, | ||
1322 | 660 | http_method="GET", http_url=url) | ||
1323 | 661 | oauth_req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), | ||
1324 | 662 | consumer, token) | ||
1325 | 663 | request = urllib2.Request(url, headers=oauth_req.to_header()) | ||
1326 | 664 | logger.debug('Opening the ping url %s with urllib2.urlopen. ' \ | ||
1327 | 665 | 'Request to: %s', PING_URL, request.get_full_url()) | ||
1328 | 666 | response = urllib2.urlopen(request) | ||
1329 | 667 | logger.debug('Url opened. Response: %s.', response.code) | ||
1330 | 668 | return response.code | ||
1331 | 669 | |||
1332 | 670 | def _show_login_or_register_ui(self, app_name, tc_url, help_text, | ||
1333 | 671 | win_id, login_only=False): | ||
1334 | 672 | """Shows the UI so the user can login or register.""" | ||
1335 | 673 | try: | ||
1336 | 674 | # delay gui import to be able to function on non-graphical envs | ||
1337 | 675 | from ubuntu_sso import gui | ||
1338 | 676 | gui_app = gui.UbuntuSSOClientGUI(app_name, tc_url, | ||
1339 | 677 | help_text, win_id, login_only) | ||
1340 | 678 | gui_app.connect(gui.SIG_LOGIN_SUCCEEDED, self._login_success_cb) | ||
1341 | 679 | gui_app.connect(gui.SIG_LOGIN_FAILED, self._login_error_cb) | ||
1342 | 680 | gui_app.connect(gui.SIG_REGISTRATION_SUCCEEDED, | ||
1343 | 681 | self._login_success_cb) | ||
1344 | 682 | gui_app.connect(gui.SIG_REGISTRATION_FAILED, self._login_error_cb) | ||
1345 | 683 | gui_app.connect(gui.SIG_USER_CANCELATION, | ||
1346 | 684 | self._login_auth_denied_cb) | ||
1347 | 685 | except: # pylint: disable=W0702 | ||
1348 | 686 | msg = '_show_login_or_register_ui failed when calling ' \ | ||
1349 | 687 | 'gui.UbuntuSSOClientGUI(%r, %r, %r, %r, %r)' | ||
1350 | 688 | logger.exception(msg, app_name, tc_url, help_text, | ||
1351 | 689 | win_id, login_only) | ||
1352 | 690 | msg = "Problem opening the Ubuntu SSO user interface." | ||
1353 | 691 | self.CredentialsError(app_name, msg, traceback.format_exc()) | ||
1354 | 692 | >>>>>>> MERGE-SOURCE | ||
1355 | 251 | 693 | ||
1356 | 252 | @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME, | 694 | @dbus.service.method(dbus_interface=DBUS_IFACE_CRED_NAME, |
1357 | 253 | in_signature="sssx", out_signature="") | 695 | in_signature="sssx", out_signature="") |
1358 | @@ -296,6 +738,7 @@ | |||
1359 | 296 | 738 | ||
1360 | 297 | 'app_name' is the name of the application. | 739 | 'app_name' is the name of the application. |
1361 | 298 | """ | 740 | """ |
1362 | 741 | <<<<<<< TREE | ||
1363 | 299 | self.root.clear_token(app_name, callback, errback) | 742 | self.root.clear_token(app_name, callback, errback) |
1364 | 300 | 743 | ||
1365 | 301 | 744 | ||
1366 | @@ -452,3 +895,13 @@ | |||
1367 | 452 | def login(self, app_name, args): | 895 | def login(self, app_name, args): |
1368 | 453 | """Get credentials if found else prompt GUI to login.""" | 896 | """Get credentials if found else prompt GUI to login.""" |
1369 | 454 | self.root.login(app_name, args) | 897 | self.root.login(app_name, args) |
1370 | 898 | ======= | ||
1371 | 899 | logger.info('Clearing credentials for app %r.', app_name) | ||
1372 | 900 | try: | ||
1373 | 901 | creds = Keyring(app_name) | ||
1374 | 902 | creds.delete_ubuntusso_attr() | ||
1375 | 903 | except: # pylint: disable=W0702 | ||
1376 | 904 | logger.exception( | ||
1377 | 905 | "problem removing credentials from keyring for %s", | ||
1378 | 906 | app_name) | ||
1379 | 907 | >>>>>>> MERGE-SOURCE | ||
1380 | 455 | 908 | ||
1381 | === modified file 'ubuntu_sso/main/tests/test_linux.py' | |||
1382 | --- ubuntu_sso/main/tests/test_linux.py 2011-03-29 14:16:36 +0000 | |||
1383 | +++ ubuntu_sso/main/tests/test_linux.py 2011-03-29 17:35:28 +0000 | |||
1384 | @@ -50,6 +50,369 @@ | |||
1385 | 50 | # pylint: disable=W0212 | 50 | # pylint: disable=W0212 |
1386 | 51 | 51 | ||
1387 | 52 | 52 | ||
1388 | 53 | <<<<<<< TREE | ||
1389 | 54 | ======= | ||
1390 | 55 | APP_NAME = 'The Coolest App Ever' | ||
1391 | 56 | CAPTCHA_PATH = os.path.abspath(os.path.join(os.curdir, 'ubuntu_sso', 'tests', | ||
1392 | 57 | 'files', 'captcha.png')) | ||
1393 | 58 | CAPTCHA_ID = 'test' | ||
1394 | 59 | CAPTCHA_SOLUTION = 'william Byrd' | ||
1395 | 60 | CANT_RESET_PASSWORD_CONTENT = "CanNotResetPassowrdError: " \ | ||
1396 | 61 | "Can't reset password for this account" | ||
1397 | 62 | RESET_TOKEN_INVALID_CONTENT = "AuthToken matching query does not exist." | ||
1398 | 63 | EMAIL = 'test@example.com' | ||
1399 | 64 | EMAIL_ALREADY_REGISTERED = 'a@example.com' | ||
1400 | 65 | EMAIL_TOKEN = 'B2Pgtf' | ||
1401 | 66 | HELP = 'help text' | ||
1402 | 67 | NAME = 'Juanito Pérez' | ||
1403 | 68 | PASSWORD = 'be4tiFul' | ||
1404 | 69 | RESET_PASSWORD_TOKEN = '8G5Wtq' | ||
1405 | 70 | TOKEN = {u'consumer_key': u'xQ7xDAz', | ||
1406 | 71 | u'consumer_secret': u'KzCJWCTNbbntwfyCKKjomJDzlgqxLy', | ||
1407 | 72 | u'token_name': u'test', | ||
1408 | 73 | u'token': u'GkInOfSMGwTXAUoVQwLUoPxElEEUdhsLVNTPhxHJDUIeHCPNEo', | ||
1409 | 74 | u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'} | ||
1410 | 75 | TOKEN_NAME = get_token_name(APP_NAME) | ||
1411 | 76 | STATUS_UNKNOWN = {'status': 'yadda-yadda'} | ||
1412 | 77 | STATUS_ERROR = {'status': 'error', 'errors': {'something': ['Bla', 'Ble']}} | ||
1413 | 78 | STATUS_OK = {'status': 'ok'} | ||
1414 | 79 | STATUS_EMAIL_UNKNOWN = {'status': 'yadda-yadda'} | ||
1415 | 80 | STATUS_EMAIL_ERROR = {'errors': {'email_token': ['Error1', 'Error2']}} | ||
1416 | 81 | STATUS_EMAIL_OK = {'email': EMAIL} | ||
1417 | 82 | TC_URL = 'tcurl' | ||
1418 | 83 | WINDOW_ID = 5 | ||
1419 | 84 | |||
1420 | 85 | NO_OP = lambda *args, **kwargs: None | ||
1421 | 86 | LOGIN_OR_REGISTER_ARGS = (APP_NAME, TC_URL, HELP, WINDOW_ID) | ||
1422 | 87 | LOGIN_OR_REGISTER_GUI_ARGS = LOGIN_OR_REGISTER_ARGS + (False,) | ||
1423 | 88 | LOGIN_ONLY_ARGS = (APP_NAME, HELP, WINDOW_ID) | ||
1424 | 89 | LOGIN_ONLY_GUI_ARGS = (APP_NAME, None, HELP, WINDOW_ID, True) | ||
1425 | 90 | |||
1426 | 91 | |||
1427 | 92 | class FakedResponse(object): | ||
1428 | 93 | """Fake a urlopen response.""" | ||
1429 | 94 | |||
1430 | 95 | def __init__(self, *args, **kwargs): | ||
1431 | 96 | for k, val in kwargs.iteritems(): | ||
1432 | 97 | setattr(self, k, val) | ||
1433 | 98 | |||
1434 | 99 | |||
1435 | 100 | class FakedCaptchas(object): | ||
1436 | 101 | """Fake the captcha generator.""" | ||
1437 | 102 | |||
1438 | 103 | def new(self): | ||
1439 | 104 | """Return a fix captcha).""" | ||
1440 | 105 | return {'image_url': 'file://%s' % CAPTCHA_PATH, | ||
1441 | 106 | 'captcha_id': CAPTCHA_ID} | ||
1442 | 107 | |||
1443 | 108 | |||
1444 | 109 | class FakedRegistrations(object): | ||
1445 | 110 | """Fake the registrations service.""" | ||
1446 | 111 | |||
1447 | 112 | def register(self, email, password, displayname, | ||
1448 | 113 | captcha_id, captcha_solution): | ||
1449 | 114 | """Fake registration. Return a fix result.""" | ||
1450 | 115 | if email == EMAIL_ALREADY_REGISTERED: | ||
1451 | 116 | return {'status': 'error', | ||
1452 | 117 | 'errors': {'email': 'Email already registered'}} | ||
1453 | 118 | elif captcha_id is None and captcha_solution is None: | ||
1454 | 119 | return STATUS_UNKNOWN | ||
1455 | 120 | elif captcha_id != CAPTCHA_ID or captcha_solution != CAPTCHA_SOLUTION: | ||
1456 | 121 | return STATUS_ERROR | ||
1457 | 122 | else: | ||
1458 | 123 | return STATUS_OK | ||
1459 | 124 | |||
1460 | 125 | def request_password_reset_token(self, email): | ||
1461 | 126 | """Fake password reset token. Return a fix result.""" | ||
1462 | 127 | if email is None: | ||
1463 | 128 | return STATUS_UNKNOWN | ||
1464 | 129 | elif email != EMAIL: | ||
1465 | 130 | raise HTTPError(response=None, content=CANT_RESET_PASSWORD_CONTENT) | ||
1466 | 131 | else: | ||
1467 | 132 | return STATUS_OK | ||
1468 | 133 | |||
1469 | 134 | def set_new_password(self, email, token, new_password): | ||
1470 | 135 | """Fake the setting of new password. Return a fix result.""" | ||
1471 | 136 | if email is None and token is None and new_password is None: | ||
1472 | 137 | return STATUS_UNKNOWN | ||
1473 | 138 | elif email != EMAIL or token != RESET_PASSWORD_TOKEN: | ||
1474 | 139 | raise HTTPError(response=None, content=RESET_TOKEN_INVALID_CONTENT) | ||
1475 | 140 | else: | ||
1476 | 141 | return STATUS_OK | ||
1477 | 142 | |||
1478 | 143 | |||
1479 | 144 | class FakedAuthentications(object): | ||
1480 | 145 | """Fake the authentications service.""" | ||
1481 | 146 | |||
1482 | 147 | def authenticate(self, token_name): | ||
1483 | 148 | """Fake authenticate. Return a fix result.""" | ||
1484 | 149 | if not token_name.startswith(TOKEN_NAME): | ||
1485 | 150 | raise HTTPError(response=None, content=None) | ||
1486 | 151 | else: | ||
1487 | 152 | return TOKEN | ||
1488 | 153 | |||
1489 | 154 | |||
1490 | 155 | class FakedAccounts(object): | ||
1491 | 156 | """Fake the accounts service.""" | ||
1492 | 157 | |||
1493 | 158 | def __init__(self): | ||
1494 | 159 | self.preferred_email = EMAIL | ||
1495 | 160 | |||
1496 | 161 | def validate_email(self, email_token): | ||
1497 | 162 | """Fake the email validation. Return a fix result.""" | ||
1498 | 163 | if email_token is None: | ||
1499 | 164 | return STATUS_EMAIL_UNKNOWN | ||
1500 | 165 | elif email_token == EMAIL_ALREADY_REGISTERED: | ||
1501 | 166 | return {'status': 'error', | ||
1502 | 167 | 'errors': {'email': 'Email already registered'}} | ||
1503 | 168 | elif email_token != EMAIL_TOKEN: | ||
1504 | 169 | return STATUS_EMAIL_ERROR | ||
1505 | 170 | else: | ||
1506 | 171 | return STATUS_EMAIL_OK | ||
1507 | 172 | |||
1508 | 173 | # pylint: disable=E0202, C0103 | ||
1509 | 174 | |||
1510 | 175 | def me(self): | ||
1511 | 176 | """Fake the 'me' information.""" | ||
1512 | 177 | return {u'username': u'Wh46bKY', | ||
1513 | 178 | u'preferred_email': self.preferred_email, | ||
1514 | 179 | u'displayname': u'', | ||
1515 | 180 | u'unverified_emails': [u'aaaaaa@example.com'], | ||
1516 | 181 | u'verified_emails': [], | ||
1517 | 182 | u'openid_identifier': u'Wh46bKY'} | ||
1518 | 183 | |||
1519 | 184 | |||
1520 | 185 | class FakedSSOServer(object): | ||
1521 | 186 | """Fake an SSO server.""" | ||
1522 | 187 | |||
1523 | 188 | def __init__(self, authorizer, service_root): | ||
1524 | 189 | self.captchas = FakedCaptchas() | ||
1525 | 190 | self.registrations = FakedRegistrations() | ||
1526 | 191 | self.authentications = FakedAuthentications() | ||
1527 | 192 | self.accounts = FakedAccounts() | ||
1528 | 193 | |||
1529 | 194 | |||
1530 | 195 | class SSOLoginProcessorTestCase(TestCase, MockerTestCase): | ||
1531 | 196 | """Test suite for the SSO login processor.""" | ||
1532 | 197 | |||
1533 | 198 | def setUp(self): | ||
1534 | 199 | """Init.""" | ||
1535 | 200 | self.processor = SSOLoginProcessor(sso_service_class=FakedSSOServer) | ||
1536 | 201 | self.register_kwargs = dict(email=EMAIL, password=PASSWORD, | ||
1537 | 202 | captcha_id=CAPTCHA_ID, | ||
1538 | 203 | captcha_solution=CAPTCHA_SOLUTION) | ||
1539 | 204 | self.login_kwargs = dict(email=EMAIL, password=PASSWORD, | ||
1540 | 205 | token_name=TOKEN_NAME) | ||
1541 | 206 | |||
1542 | 207 | def tearDown(self): | ||
1543 | 208 | """Clean up.""" | ||
1544 | 209 | self.processor = None | ||
1545 | 210 | |||
1546 | 211 | def test_generate_captcha(self): | ||
1547 | 212 | """Captcha can be generated.""" | ||
1548 | 213 | filename = self.mktemp() | ||
1549 | 214 | self.addCleanup(lambda: os.remove(filename)) | ||
1550 | 215 | captcha_id = self.processor.generate_captcha(filename) | ||
1551 | 216 | self.assertEqual(CAPTCHA_ID, captcha_id, 'captcha id must be correct.') | ||
1552 | 217 | self.assertTrue(os.path.isfile(filename), '%s must exist.' % filename) | ||
1553 | 218 | |||
1554 | 219 | with open(CAPTCHA_PATH) as f: | ||
1555 | 220 | expected = f.read() | ||
1556 | 221 | with open(filename) as f: | ||
1557 | 222 | actual = f.read() | ||
1558 | 223 | self.assertEqual(expected, actual, 'captcha image must be correct.') | ||
1559 | 224 | |||
1560 | 225 | def test_register_user_checks_valid_email(self): | ||
1561 | 226 | """Email is validated.""" | ||
1562 | 227 | self.register_kwargs['email'] = 'notavalidemail' | ||
1563 | 228 | self.assertRaises(InvalidEmailError, | ||
1564 | 229 | self.processor.register_user, **self.register_kwargs) | ||
1565 | 230 | |||
1566 | 231 | def test_register_user_checks_valid_password(self): | ||
1567 | 232 | """Password is validated.""" | ||
1568 | 233 | self.register_kwargs['password'] = '' | ||
1569 | 234 | self.assertRaises(InvalidPasswordError, | ||
1570 | 235 | self.processor.register_user, **self.register_kwargs) | ||
1571 | 236 | |||
1572 | 237 | # 7 chars, one less than expected | ||
1573 | 238 | self.register_kwargs['password'] = 'tesT3it' | ||
1574 | 239 | self.assertRaises(InvalidPasswordError, | ||
1575 | 240 | self.processor.register_user, **self.register_kwargs) | ||
1576 | 241 | |||
1577 | 242 | self.register_kwargs['password'] = 'test3it!' # no upper case | ||
1578 | 243 | self.assertRaises(InvalidPasswordError, | ||
1579 | 244 | self.processor.register_user, **self.register_kwargs) | ||
1580 | 245 | |||
1581 | 246 | self.register_kwargs['password'] = 'testIt!!' # no number | ||
1582 | 247 | self.assertRaises(InvalidPasswordError, | ||
1583 | 248 | self.processor.register_user, **self.register_kwargs) | ||
1584 | 249 | |||
1585 | 250 | # register | ||
1586 | 251 | |||
1587 | 252 | def test_register_user_if_status_ok(self): | ||
1588 | 253 | """A user is succesfuy registered into the SSO server.""" | ||
1589 | 254 | result = self.processor.register_user(**self.register_kwargs) | ||
1590 | 255 | self.assertEqual(EMAIL, result, 'registration was successful.') | ||
1591 | 256 | |||
1592 | 257 | def test_register_user_if_status_error(self): | ||
1593 | 258 | """Proper error is raised if register fails.""" | ||
1594 | 259 | self.register_kwargs['captcha_id'] = CAPTCHA_ID * 2 # incorrect | ||
1595 | 260 | failure = self.assertRaises(RegistrationError, | ||
1596 | 261 | self.processor.register_user, | ||
1597 | 262 | **self.register_kwargs) | ||
1598 | 263 | for k, val in failure.args[0].items(): | ||
1599 | 264 | self.assertIn(k, STATUS_ERROR['errors']) | ||
1600 | 265 | self.assertEqual(val, "\n".join(STATUS_ERROR['errors'][k])) | ||
1601 | 266 | |||
1602 | 267 | def test_register_user_if_status_error_with_string_message(self): | ||
1603 | 268 | """Proper error is raised if register fails.""" | ||
1604 | 269 | self.register_kwargs['email'] = EMAIL_ALREADY_REGISTERED | ||
1605 | 270 | failure = self.assertRaises(RegistrationError, | ||
1606 | 271 | self.processor.register_user, | ||
1607 | 272 | **self.register_kwargs) | ||
1608 | 273 | for k, val in failure.args[0].items(): | ||
1609 | 274 | self.assertIn(k, {'email': 'Email already registered'}) | ||
1610 | 275 | self.assertEqual(val, 'Email already registered') | ||
1611 | 276 | |||
1612 | 277 | def test_register_user_if_status_unknown(self): | ||
1613 | 278 | """Proper error is raised if register returns an unknown status.""" | ||
1614 | 279 | self.register_kwargs['captcha_id'] = None | ||
1615 | 280 | self.register_kwargs['captcha_solution'] = None | ||
1616 | 281 | failure = self.assertRaises(RegistrationError, | ||
1617 | 282 | self.processor.register_user, | ||
1618 | 283 | **self.register_kwargs) | ||
1619 | 284 | self.assertIn('Received unknown status: %s' % STATUS_UNKNOWN, failure) | ||
1620 | 285 | |||
1621 | 286 | # login | ||
1622 | 287 | |||
1623 | 288 | def test_login_if_http_error(self): | ||
1624 | 289 | """Proper error is raised if authentication fails.""" | ||
1625 | 290 | self.login_kwargs['token_name'] = APP_NAME * 2 # invalid token name | ||
1626 | 291 | self.assertRaises(AuthenticationError, | ||
1627 | 292 | self.processor.login, **self.login_kwargs) | ||
1628 | 293 | |||
1629 | 294 | def test_login_if_no_error(self): | ||
1630 | 295 | """A user can be succesfully logged in into the SSO service.""" | ||
1631 | 296 | result = self.processor.login(**self.login_kwargs) | ||
1632 | 297 | self.assertEqual(TOKEN, result, 'authentication was successful.') | ||
1633 | 298 | |||
1634 | 299 | # is_validated | ||
1635 | 300 | |||
1636 | 301 | def test_is_validated(self): | ||
1637 | 302 | """If preferred email is not None, user is validated.""" | ||
1638 | 303 | result = self.processor.is_validated(token=TOKEN) | ||
1639 | 304 | self.assertTrue(result, 'user must be validated.') | ||
1640 | 305 | |||
1641 | 306 | def test_is_not_validated(self): | ||
1642 | 307 | """If preferred email is None, user is not validated.""" | ||
1643 | 308 | service = FakedSSOServer(None, None) | ||
1644 | 309 | service.accounts.preferred_email = None | ||
1645 | 310 | result = self.processor.is_validated(sso_service=service, | ||
1646 | 311 | token=TOKEN) | ||
1647 | 312 | self.assertFalse(result, 'user must not be validated.') | ||
1648 | 313 | |||
1649 | 314 | def test_is_not_validated_empty_result(self): | ||
1650 | 315 | """If preferred email is None, user is not validated.""" | ||
1651 | 316 | service = FakedSSOServer(None, None) | ||
1652 | 317 | service.accounts.me = lambda: {} | ||
1653 | 318 | result = self.processor.is_validated(sso_service=service, | ||
1654 | 319 | token=TOKEN) | ||
1655 | 320 | self.assertFalse(result, 'user must not be validated.') | ||
1656 | 321 | |||
1657 | 322 | # validate_email | ||
1658 | 323 | |||
1659 | 324 | def test_validate_email_if_status_ok(self): | ||
1660 | 325 | """A email is succesfuy validated in the SSO server.""" | ||
1661 | 326 | self.login_kwargs['email_token'] = EMAIL_TOKEN # valid email token | ||
1662 | 327 | result = self.processor.validate_email(**self.login_kwargs) | ||
1663 | 328 | self.assertEqual(TOKEN, result, 'email validation was successful.') | ||
1664 | 329 | |||
1665 | 330 | def test_validate_email_if_status_error(self): | ||
1666 | 331 | """Proper error is raised if email validation fails.""" | ||
1667 | 332 | self.login_kwargs['email_token'] = EMAIL_TOKEN * 2 # invalid token | ||
1668 | 333 | failure = self.assertRaises(EmailTokenError, | ||
1669 | 334 | self.processor.validate_email, | ||
1670 | 335 | **self.login_kwargs) | ||
1671 | 336 | for k, val in failure.args[0].items(): | ||
1672 | 337 | self.assertIn(k, STATUS_EMAIL_ERROR['errors']) | ||
1673 | 338 | self.assertEqual(val, "\n".join(STATUS_EMAIL_ERROR['errors'][k])) | ||
1674 | 339 | |||
1675 | 340 | def test_validate_email_if_status_error_with_string_message(self): | ||
1676 | 341 | """Proper error is raised if register fails.""" | ||
1677 | 342 | self.login_kwargs['email_token'] = EMAIL_ALREADY_REGISTERED | ||
1678 | 343 | failure = self.assertRaises(EmailTokenError, | ||
1679 | 344 | self.processor.validate_email, | ||
1680 | 345 | **self.login_kwargs) | ||
1681 | 346 | for k, val in failure.args[0].items(): | ||
1682 | 347 | self.assertIn(k, {'email': 'Email already registered'}) | ||
1683 | 348 | self.assertEqual(val, 'Email already registered') | ||
1684 | 349 | |||
1685 | 350 | def test_validate_email_if_status_unknown(self): | ||
1686 | 351 | """Proper error is raised if email validation returns unknown.""" | ||
1687 | 352 | self.login_kwargs['email_token'] = None | ||
1688 | 353 | failure = self.assertRaises(EmailTokenError, | ||
1689 | 354 | self.processor.validate_email, | ||
1690 | 355 | **self.login_kwargs) | ||
1691 | 356 | self.assertIn('Received invalid reply: %s' % STATUS_UNKNOWN, failure) | ||
1692 | 357 | |||
1693 | 358 | # reset_password | ||
1694 | 359 | |||
1695 | 360 | def test_request_password_reset_token_if_status_ok(self): | ||
1696 | 361 | """A reset password token is succesfuly sent.""" | ||
1697 | 362 | result = self.processor.request_password_reset_token(email=EMAIL) | ||
1698 | 363 | self.assertEqual(EMAIL, result, | ||
1699 | 364 | 'password reset token must be successful.') | ||
1700 | 365 | |||
1701 | 366 | def test_request_password_reset_token_if_http_error(self): | ||
1702 | 367 | """Proper error is raised if password token request fails.""" | ||
1703 | 368 | exc = self.assertRaises(ResetPasswordTokenError, | ||
1704 | 369 | self.processor.request_password_reset_token, | ||
1705 | 370 | email=EMAIL * 2) | ||
1706 | 371 | self.assertIn(CANT_RESET_PASSWORD_CONTENT, exc) | ||
1707 | 372 | |||
1708 | 373 | def test_request_password_reset_token_if_status_unknown(self): | ||
1709 | 374 | """Proper error is raised if password token request returns unknown.""" | ||
1710 | 375 | exc = self.assertRaises(ResetPasswordTokenError, | ||
1711 | 376 | self.processor.request_password_reset_token, | ||
1712 | 377 | email=None) | ||
1713 | 378 | self.assertIn('Received invalid reply: %s' % STATUS_UNKNOWN, exc) | ||
1714 | 379 | |||
1715 | 380 | def test_set_new_password_if_status_ok(self): | ||
1716 | 381 | """A new password is succesfuy set.""" | ||
1717 | 382 | result = self.processor.set_new_password(email=EMAIL, | ||
1718 | 383 | token=RESET_PASSWORD_TOKEN, | ||
1719 | 384 | new_password=PASSWORD) | ||
1720 | 385 | self.assertEqual(EMAIL, result, | ||
1721 | 386 | 'new password must be set successfully.') | ||
1722 | 387 | |||
1723 | 388 | def test_set_new_password_if_http_error(self): | ||
1724 | 389 | """Proper error is raised if setting a new password fails.""" | ||
1725 | 390 | exc = self.assertRaises(NewPasswordError, | ||
1726 | 391 | self.processor.set_new_password, | ||
1727 | 392 | email=EMAIL * 2, | ||
1728 | 393 | token=RESET_PASSWORD_TOKEN * 2, | ||
1729 | 394 | new_password=PASSWORD) | ||
1730 | 395 | self.assertIn(RESET_TOKEN_INVALID_CONTENT, exc) | ||
1731 | 396 | |||
1732 | 397 | def test_set_new_password_if_status_unknown(self): | ||
1733 | 398 | """Proper error is raised if setting a new password returns unknown.""" | ||
1734 | 399 | exc = self.assertRaises(NewPasswordError, | ||
1735 | 400 | self.processor.set_new_password, | ||
1736 | 401 | email=None, token=None, new_password=None) | ||
1737 | 402 | self.assertIn('Received invalid reply: %s' % STATUS_UNKNOWN, exc) | ||
1738 | 403 | |||
1739 | 404 | |||
1740 | 405 | class SSORegistrationWithNameTestCase(SSOLoginProcessorTestCase): | ||
1741 | 406 | """Test suite for the SSO login processor for register_with_name.""" | ||
1742 | 407 | |||
1743 | 408 | def setUp(self): | ||
1744 | 409 | """Init.""" | ||
1745 | 410 | super(SSORegistrationWithNameTestCase, self).setUp() | ||
1746 | 411 | self.register_kwargs['displayname'] = NAME | ||
1747 | 412 | self.processor.register_user = self.processor.register_with_name | ||
1748 | 413 | |||
1749 | 414 | |||
1750 | 415 | >>>>>>> MERGE-SOURCE | ||
1751 | 53 | class BlockingSampleException(Exception): | 416 | class BlockingSampleException(Exception): |
1752 | 54 | """The exception that will be thrown by the fake blocking.""" | 417 | """The exception that will be thrown by the fake blocking.""" |
1753 | 55 | 418 | ||
1754 | @@ -84,13 +447,21 @@ | |||
1755 | 84 | mockbus._register_object_path(ARGS) | 447 | mockbus._register_object_path(ARGS) |
1756 | 85 | self.mockprocessorclass = None | 448 | self.mockprocessorclass = None |
1757 | 86 | 449 | ||
1758 | 450 | <<<<<<< TREE | ||
1759 | 87 | def ksc(keyring, k, val): | 451 | def ksc(keyring, k, val): |
1760 | 452 | ======= | ||
1761 | 453 | def ksc(k, val, callback, *cb_args): | ||
1762 | 454 | >>>>>>> MERGE-SOURCE | ||
1763 | 88 | """Assert over token and app_name.""" | 455 | """Assert over token and app_name.""" |
1764 | 89 | self.assertEqual(k, APP_NAME) | 456 | self.assertEqual(k, APP_NAME) |
1765 | 90 | self.assertEqual(val, TOKEN) | 457 | self.assertEqual(val, TOKEN) |
1766 | 91 | self.keyring_was_set = True | 458 | self.keyring_was_set = True |
1767 | 92 | self.keyring_values = k, val | 459 | self.keyring_values = k, val |
1768 | 460 | <<<<<<< TREE | ||
1769 | 93 | return defer.succeed(None) | 461 | return defer.succeed(None) |
1770 | 462 | ======= | ||
1771 | 463 | callback(*cb_args) | ||
1772 | 464 | >>>>>>> MERGE-SOURCE | ||
1773 | 94 | 465 | ||
1774 | 95 | self.patch(ubuntu_sso.main.Keyring, "set_credentials", ksc) | 466 | self.patch(ubuntu_sso.main.Keyring, "set_credentials", ksc) |
1775 | 96 | self.keyring_was_set = False | 467 | self.keyring_was_set = False |
1776 | @@ -164,8 +535,13 @@ | |||
1777 | 164 | """Test that the register_user method works ok.""" | 535 | """Test that the register_user method works ok.""" |
1778 | 165 | d = Deferred() | 536 | d = Deferred() |
1779 | 166 | expected_result = "expected result" | 537 | expected_result = "expected result" |
1780 | 538 | <<<<<<< TREE | ||
1781 | 167 | self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME, | 539 | self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME, |
1782 | 168 | CAPTCHA_ID, CAPTCHA_SOLUTION) | 540 | CAPTCHA_ID, CAPTCHA_SOLUTION) |
1783 | 541 | ======= | ||
1784 | 542 | self.create_mock_processor().register_user(EMAIL, PASSWORD, | ||
1785 | 543 | CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
1786 | 544 | >>>>>>> MERGE-SOURCE | ||
1787 | 169 | self.mocker.result(expected_result) | 545 | self.mocker.result(expected_result) |
1788 | 170 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | 546 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) |
1789 | 171 | self.mocker.replay() | 547 | self.mocker.replay() |
1790 | @@ -188,8 +564,13 @@ | |||
1791 | 188 | """Test that the register_user method fails as expected.""" | 564 | """Test that the register_user method fails as expected.""" |
1792 | 189 | d = Deferred() | 565 | d = Deferred() |
1793 | 190 | expected_result = "expected result" | 566 | expected_result = "expected result" |
1794 | 567 | <<<<<<< TREE | ||
1795 | 191 | self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME, | 568 | self.create_mock_processor().register_user(EMAIL, PASSWORD, NAME, |
1796 | 192 | CAPTCHA_ID, CAPTCHA_SOLUTION) | 569 | CAPTCHA_ID, CAPTCHA_SOLUTION) |
1797 | 570 | ======= | ||
1798 | 571 | self.create_mock_processor().register_user(EMAIL, PASSWORD, | ||
1799 | 572 | CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
1800 | 573 | >>>>>>> MERGE-SOURCE | ||
1801 | 193 | self.mocker.result(expected_result) | 574 | self.mocker.result(expected_result) |
1802 | 194 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) | 575 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) |
1803 | 195 | self.mocker.replay() | 576 | self.mocker.replay() |
1804 | @@ -208,15 +589,69 @@ | |||
1805 | 208 | CAPTCHA_SOLUTION) | 589 | CAPTCHA_SOLUTION) |
1806 | 209 | return d | 590 | return d |
1807 | 210 | 591 | ||
1808 | 592 | def test_register_with_name(self): | ||
1809 | 593 | """Test that the register_with_name method works ok.""" | ||
1810 | 594 | d = Deferred() | ||
1811 | 595 | expected_result = "expected result" | ||
1812 | 596 | self.create_mock_processor().register_with_name(EMAIL, PASSWORD, NAME, | ||
1813 | 597 | CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
1814 | 598 | self.mocker.result(expected_result) | ||
1815 | 599 | self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) | ||
1816 | 600 | self.mocker.replay() | ||
1817 | 601 | |||
1818 | 602 | def verify(app_name, result): | ||
1819 | 603 | """The actual test.""" | ||
1820 | 604 | self.assertEqual(result, expected_result) | ||
1821 | 605 | self.assertEqual(app_name, APP_NAME) | ||
1822 | 606 | d.callback(result) | ||
1823 | 607 | |||
1824 | 608 | client = SSOLogin(self.mockbusname, | ||
1825 | 609 | sso_login_processor_class=self.mockprocessorclass) | ||
1826 | 610 | self.patch(client, "UserRegistered", verify) | ||
1827 | 611 | self.patch(client, "UserRegistrationError", d.errback) | ||
1828 | 612 | client.register_with_name(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, | ||
1829 | 613 | CAPTCHA_SOLUTION) | ||
1830 | 614 | return d | ||
1831 | 615 | |||
1832 | 616 | def test_register_with_name_error(self): | ||
1833 | 617 | """Test that the register_with_name method fails as expected.""" | ||
1834 | 618 | d = Deferred() | ||
1835 | 619 | expected_result = "expected result" | ||
1836 | 620 | self.create_mock_processor().register_with_name(EMAIL, PASSWORD, NAME, | ||
1837 | 621 | CAPTCHA_ID, CAPTCHA_SOLUTION) | ||
1838 | 622 | self.mocker.result(expected_result) | ||
1839 | 623 | self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) | ||
1840 | 624 | self.mocker.replay() | ||
1841 | 625 | |||
1842 | 626 | def verify(app_name, errdict): | ||
1843 | 627 | """The actual test.""" | ||
1844 | 628 | self.assertEqual(errdict["errtype"], "BlockingSampleException") | ||
1845 | 629 | self.assertEqual(app_name, APP_NAME) | ||
1846 | 630 | d.callback("Ok") | ||
1847 | 631 | |||
1848 | 632 | client = SSOLogin(self.mockbusname, | ||
1849 | 633 | sso_login_processor_class=self.mockprocessorclass) | ||
1850 | 634 | self.patch(client, "UserRegistered", d.errback) | ||
1851 | 635 | self.patch(client, "UserRegistrationError", verify) | ||
1852 | 636 | client.register_with_name(APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, | ||
1853 | 637 | CAPTCHA_SOLUTION) | ||
1854 | 638 | return d | ||
1855 | 639 | |||
1856 | 211 | def test_login(self): | 640 | def test_login(self): |
1857 | 212 | """Test that the login method works ok.""" | 641 | """Test that the login method works ok.""" |
1858 | 213 | d = Deferred() | 642 | d = Deferred() |
1859 | 214 | processor = self.create_mock_processor() | 643 | processor = self.create_mock_processor() |
1860 | 215 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) | 644 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) |
1861 | 216 | self.mocker.result(TOKEN) | 645 | self.mocker.result(TOKEN) |
1862 | 646 | <<<<<<< TREE | ||
1863 | 217 | processor.is_validated(TOKEN) | 647 | processor.is_validated(TOKEN) |
1864 | 218 | self.mocker.result(True) | 648 | self.mocker.result(True) |
1865 | 219 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) | 649 | self.patch(ubuntu_sso.main.linux, "blocking", fake_ok_blocking) |
1866 | 650 | ======= | ||
1867 | 651 | processor.is_validated(TOKEN) | ||
1868 | 652 | self.mocker.result(True) | ||
1869 | 653 | self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) | ||
1870 | 654 | >>>>>>> MERGE-SOURCE | ||
1871 | 220 | self.mocker.replay() | 655 | self.mocker.replay() |
1872 | 221 | 656 | ||
1873 | 222 | def verify(app_name, result): | 657 | def verify(app_name, result): |
1874 | @@ -230,6 +665,7 @@ | |||
1875 | 230 | sso_login_processor_class=self.mockprocessorclass) | 665 | sso_login_processor_class=self.mockprocessorclass) |
1876 | 231 | self.patch(client, "LoggedIn", verify) | 666 | self.patch(client, "LoggedIn", verify) |
1877 | 232 | self.patch(client, "LoginError", d.errback) | 667 | self.patch(client, "LoginError", d.errback) |
1878 | 668 | <<<<<<< TREE | ||
1879 | 233 | self.patch(client, "UserNotValidated", d.errback) | 669 | self.patch(client, "UserNotValidated", d.errback) |
1880 | 234 | client.login(APP_NAME, EMAIL, PASSWORD) | 670 | client.login(APP_NAME, EMAIL, PASSWORD) |
1881 | 235 | return d | 671 | return d |
1882 | @@ -265,6 +701,43 @@ | |||
1883 | 265 | d = Deferred() | 701 | d = Deferred() |
1884 | 266 | self.create_mock_processor() | 702 | self.create_mock_processor() |
1885 | 267 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) | 703 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) |
1886 | 704 | ======= | ||
1887 | 705 | self.patch(client, "UserNotValidated", d.errback) | ||
1888 | 706 | client.login(APP_NAME, EMAIL, PASSWORD) | ||
1889 | 707 | return d | ||
1890 | 708 | |||
1891 | 709 | def test_login_user_not_validated(self): | ||
1892 | 710 | """Test that the login sends EmailNotValidated signal.""" | ||
1893 | 711 | d = Deferred() | ||
1894 | 712 | processor = self.create_mock_processor() | ||
1895 | 713 | processor.login(EMAIL, PASSWORD, TOKEN_NAME) | ||
1896 | 714 | self.mocker.result(TOKEN) | ||
1897 | 715 | processor.is_validated(TOKEN) | ||
1898 | 716 | self.mocker.result(False) | ||
1899 | 717 | self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) | ||
1900 | 718 | self.mocker.replay() | ||
1901 | 719 | |||
1902 | 720 | def verify(app_name, email): | ||
1903 | 721 | """The actual test.""" | ||
1904 | 722 | self.assertEqual(app_name, APP_NAME) | ||
1905 | 723 | self.assertEqual(email, EMAIL) | ||
1906 | 724 | self.assertFalse(self.keyring_was_set, "Keyring should not be set") | ||
1907 | 725 | d.callback("Ok") | ||
1908 | 726 | |||
1909 | 727 | client = SSOLogin(self.mockbusname, | ||
1910 | 728 | sso_login_processor_class=self.mockprocessorclass) | ||
1911 | 729 | self.patch(client, "LoggedIn", d.errback) | ||
1912 | 730 | self.patch(client, "LoginError", d.errback) | ||
1913 | 731 | self.patch(client, "UserNotValidated", verify) | ||
1914 | 732 | client.login(APP_NAME, EMAIL, PASSWORD) | ||
1915 | 733 | return d | ||
1916 | 734 | |||
1917 | 735 | def test_login_error(self): | ||
1918 | 736 | """Test that the login method fails as expected.""" | ||
1919 | 737 | d = Deferred() | ||
1920 | 738 | self.create_mock_processor() | ||
1921 | 739 | self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) | ||
1922 | 740 | >>>>>>> MERGE-SOURCE | ||
1923 | 268 | 741 | ||
1924 | 269 | def fake_gtn(*args): | 742 | def fake_gtn(*args): |
1925 | 270 | """A fake get_token_name that fails.""" | 743 | """A fake get_token_name that fails.""" |
1926 | @@ -284,6 +757,7 @@ | |||
1927 | 284 | sso_login_processor_class=self.mockprocessorclass) | 757 | sso_login_processor_class=self.mockprocessorclass) |
1928 | 285 | self.patch(client, "LoggedIn", d.errback) | 758 | self.patch(client, "LoggedIn", d.errback) |
1929 | 286 | self.patch(client, "LoginError", verify) | 759 | self.patch(client, "LoginError", verify) |
1930 | 760 | <<<<<<< TREE | ||
1931 | 287 | self.patch(client, "UserNotValidated", d.errback) | 761 | self.patch(client, "UserNotValidated", d.errback) |
1932 | 288 | client.login(APP_NAME, EMAIL, PASSWORD) | 762 | client.login(APP_NAME, EMAIL, PASSWORD) |
1933 | 289 | return d | 763 | return d |
1934 | @@ -319,6 +793,9 @@ | |||
1935 | 319 | self.patch(client, "LoggedIn", fail) | 793 | self.patch(client, "LoggedIn", fail) |
1936 | 320 | self.patch(client, "LoginError", verify) | 794 | self.patch(client, "LoginError", verify) |
1937 | 321 | self.patch(client, "UserNotValidated", fail) | 795 | self.patch(client, "UserNotValidated", fail) |
1938 | 796 | ======= | ||
1939 | 797 | self.patch(client, "UserNotValidated", d.errback) | ||
1940 | 798 | >>>>>>> MERGE-SOURCE | ||
1941 | 322 | client.login(APP_NAME, EMAIL, PASSWORD) | 799 | client.login(APP_NAME, EMAIL, PASSWORD) |
1942 | 323 | return d | 800 | return d |
1943 | 324 | 801 | ||
1944 | @@ -348,8 +825,13 @@ | |||
1945 | 348 | def test_validate_email_error(self): | 825 | def test_validate_email_error(self): |
1946 | 349 | """Test that the validate_email method fails as expected.""" | 826 | """Test that the validate_email method fails as expected.""" |
1947 | 350 | d = Deferred() | 827 | d = Deferred() |
1948 | 828 | <<<<<<< TREE | ||
1949 | 351 | self.create_mock_processor() | 829 | self.create_mock_processor() |
1950 | 352 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) | 830 | self.patch(ubuntu_sso.main.linux, "blocking", fake_err_blocking) |
1951 | 831 | ======= | ||
1952 | 832 | self.create_mock_processor() | ||
1953 | 833 | self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) | ||
1954 | 834 | >>>>>>> MERGE-SOURCE | ||
1955 | 353 | 835 | ||
1956 | 354 | def fake_gtn(*args): | 836 | def fake_gtn(*args): |
1957 | 355 | """A fake get_token_name that fails.""" | 837 | """A fake get_token_name that fails.""" |
1958 | @@ -559,6 +1041,58 @@ | |||
1959 | 559 | self.assertEqual(result["errtype"], e.__class__.__name__) | 1041 | self.assertEqual(result["errtype"], e.__class__.__name__) |
1960 | 560 | 1042 | ||
1961 | 561 | 1043 | ||
1962 | 1044 | <<<<<<< TREE | ||
1963 | 1045 | ======= | ||
1964 | 1046 | class KeyringCredentialsTestCase(TestCase, MockerTestCase): | ||
1965 | 1047 | """Check the functions that access the keyring.""" | ||
1966 | 1048 | |||
1967 | 1049 | # Invalid name (should match ([a-z_][a-z0-9_]*|[A-Z_][A-Z0-9_]*)$) | ||
1968 | 1050 | # pylint: disable=C0103 | ||
1969 | 1051 | |||
1970 | 1052 | def test_keyring_store_cred(self): | ||
1971 | 1053 | """Verify the method that stores credentials.""" | ||
1972 | 1054 | idle_add = lambda f, *args, **kwargs: f(*args, **kwargs) | ||
1973 | 1055 | self.patch(gobject, "idle_add", idle_add) | ||
1974 | 1056 | token_value = TOKEN | ||
1975 | 1057 | mockKeyringClass = self.mocker.replace("ubuntu_sso.keyring.Keyring") | ||
1976 | 1058 | mockKeyringClass(APP_NAME) | ||
1977 | 1059 | mockKeyring = self.mocker.mock() | ||
1978 | 1060 | callback = self.mocker.mock() | ||
1979 | 1061 | self.mocker.result(mockKeyring) | ||
1980 | 1062 | mockKeyring.set_ubuntusso_attr(token_value) | ||
1981 | 1063 | callback(1, 2, 3) | ||
1982 | 1064 | self.mocker.replay() | ||
1983 | 1065 | |||
1984 | 1066 | keyring_store_credentials(APP_NAME, token_value, callback, 1, 2, 3) | ||
1985 | 1067 | |||
1986 | 1068 | def test_keyring_get_cred(self): | ||
1987 | 1069 | """The method returns the right token.""" | ||
1988 | 1070 | mockKeyringClass = self.mocker.replace("ubuntu_sso.keyring.Keyring") | ||
1989 | 1071 | mockKeyringClass(APP_NAME) | ||
1990 | 1072 | mockKeyring = self.mocker.mock() | ||
1991 | 1073 | self.mocker.result(mockKeyring) | ||
1992 | 1074 | mockKeyring.get_ubuntusso_attr() | ||
1993 | 1075 | self.mocker.result(TOKEN) | ||
1994 | 1076 | self.mocker.replay() | ||
1995 | 1077 | |||
1996 | 1078 | token = keyring_get_credentials(APP_NAME) | ||
1997 | 1079 | self.assertEqual(token, TOKEN) | ||
1998 | 1080 | |||
1999 | 1081 | def test_keyring_get_cred_not_found(self): | ||
2000 | 1082 | """The method returns None when the token is not found.""" | ||
2001 | 1083 | mockKeyringClass = self.mocker.replace("ubuntu_sso.keyring.Keyring") | ||
2002 | 1084 | mockKeyringClass(APP_NAME) | ||
2003 | 1085 | mockKeyring = self.mocker.mock() | ||
2004 | 1086 | self.mocker.result(mockKeyring) | ||
2005 | 1087 | mockKeyring.get_ubuntusso_attr() | ||
2006 | 1088 | self.mocker.result(None) | ||
2007 | 1089 | self.mocker.replay() | ||
2008 | 1090 | |||
2009 | 1091 | token = keyring_get_credentials(APP_NAME) | ||
2010 | 1092 | self.assertEqual(token, None) | ||
2011 | 1093 | |||
2012 | 1094 | |||
2013 | 1095 | >>>>>>> MERGE-SOURCE | ||
2014 | 562 | class RegisterSampleException(Exception): | 1096 | class RegisterSampleException(Exception): |
2015 | 563 | """A mock exception thrown just when testing.""" | 1097 | """A mock exception thrown just when testing.""" |
2016 | 564 | 1098 | ||
2017 | @@ -677,6 +1211,24 @@ | |||
2018 | 677 | client = SSOCredentials(self.mocker.mock()) | 1211 | client = SSOCredentials(self.mocker.mock()) |
2019 | 678 | client.clear_token(APP_NAME) | 1212 | client.clear_token(APP_NAME) |
2020 | 679 | 1213 | ||
2021 | 1214 | def test_credentials_are_not_stored_if_ping_failed(self): | ||
2022 | 1215 | """Credentials are not stored if the ping fails.""" | ||
2023 | 1216 | |||
2024 | 1217 | def fail(*args, **kwargs): | ||
2025 | 1218 | """Raise an exception.""" | ||
2026 | 1219 | self.args = AssertionError((args, kwargs)) | ||
2027 | 1220 | # pylint: disable=E0702 | ||
2028 | 1221 | raise self.args | ||
2029 | 1222 | |||
2030 | 1223 | self.patch(self.client, '_ping_url', fail) | ||
2031 | 1224 | self._patch('clear_token') | ||
2032 | 1225 | |||
2033 | 1226 | self.client._login_success_cb(None, APP_NAME, EMAIL) | ||
2034 | 1227 | |||
2035 | 1228 | self.assertEqual(len(self.calls), 1) | ||
2036 | 1229 | self.assertEqual(self.calls[0][0], 'clear_token') | ||
2037 | 1230 | self.assertEqual(self.calls[0][1][0], APP_NAME) | ||
2038 | 1231 | |||
2039 | 680 | 1232 | ||
2040 | 681 | class EnvironOverridesTestCase(TestCase): | 1233 | class EnvironOverridesTestCase(TestCase): |
2041 | 682 | """Some URLs can be set from the environment for testing/QA purposes.""" | 1234 | """Some URLs can be set from the environment for testing/QA purposes.""" |