Merge lp:~nataliabidart/ubuntu-sso-client/new-dbus-iface into lp:ubuntu-sso-client
- new-dbus-iface
- Merge into trunk
Proposed by
Natalia Bidart
Status: | Merged |
---|---|
Approved by: | Alejandro J. Cura |
Approved revision: | 651 |
Merged at revision: | 642 |
Proposed branch: | lp:~nataliabidart/ubuntu-sso-client/new-dbus-iface |
Merge into: | lp:ubuntu-sso-client |
Diff against target: |
797 lines (+461/-65) 6 files modified
ubuntu_sso/__init__.py (+1/-6) ubuntu_sso/credentials.py (+16/-2) ubuntu_sso/main.py (+143/-6) ubuntu_sso/tests/__init__.py (+1/-1) ubuntu_sso/tests/test_credentials.py (+11/-7) ubuntu_sso/tests/test_main.py (+289/-43) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu-sso-client/new-dbus-iface |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alejandro J. Cura (community) | Approve | ||
Rodrigo Moya (community) | Approve | ||
Review via email: mp+37844@code.launchpad.net |
Commit message
Adding a new DBus iface to manage credentials with flexible API (LP: #653113).
Description of the change
Adding a new DBus iface to manage credentials. The methods in this interface have a flexible API in the sense that they accept a dictionary (string, string) as parameter so we can easily expand as necessary.
Also, {find, clear}_credentials are now not blocking operations, and the result of those are returned via DBus signals.
To post a comment you must log in.
Revision history for this message
Rodrigo Moya (rodrigo-moya) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'ubuntu_sso/__init__.py' |
2 | --- ubuntu_sso/__init__.py 2010-10-01 18:26:56 +0000 |
3 | +++ ubuntu_sso/__init__.py 2010-10-07 12:44:44 +0000 |
4 | @@ -22,11 +22,6 @@ |
5 | DBUS_IFACE_AUTH_NAME = "com.ubuntu.sso" |
6 | DBUS_IFACE_USER_NAME = "com.ubuntu.sso.UserManagement" |
7 | DBUS_IFACE_CRED_NAME = "com.ubuntu.sso.ApplicationCredentials" |
8 | +DBUS_IFACE_CREDENTIALS_NAME = "com.ubuntu.sso.CredentialsManagement" |
9 | |
10 | NO_OP = lambda *args, **kwargs: None |
11 | - |
12 | -APP_NAME_KEY = 'app_name' |
13 | -TC_URL_KEY = 'tc_url' |
14 | -HELP_TEXT_KEY = 'help_text' |
15 | -WINDOW_ID_KEY = 'window_id' |
16 | -PING_URL_KEY = 'ping_url' |
17 | |
18 | === modified file 'ubuntu_sso/credentials.py' |
19 | --- ubuntu_sso/credentials.py 2010-10-04 23:34:38 +0000 |
20 | +++ ubuntu_sso/credentials.py 2010-10-07 12:44:44 +0000 |
21 | @@ -50,6 +50,18 @@ |
22 | logger = setup_logging('ubuntu_sso.credentials') |
23 | |
24 | |
25 | +APP_NAME_KEY = 'app_name' |
26 | +TC_URL_KEY = 'tc_url' |
27 | +HELP_TEXT_KEY = 'help_text' |
28 | +WINDOW_ID_KEY = 'window_id' |
29 | +PING_URL_KEY = 'ping_url' |
30 | +SUCCESS_CB_KEY = 'success_cb' |
31 | +ERROR_CB_KEY = 'error_cb' |
32 | +DENIAL_CB_KEY = 'denial_cb' |
33 | +ERROR_KEY = 'error_message' |
34 | +ERROR_DETAIL_KEY = 'detailed_error' |
35 | + |
36 | + |
37 | def keyring_store_credentials(app_name, credentials): |
38 | """Store the credentials in the keyring.""" |
39 | logger.info('keyring_store_credentials: app_name "%s".', app_name) |
40 | @@ -91,7 +103,9 @@ |
41 | f.__name__, self.app_name, msg) |
42 | logger.error('%s (app_name: %s): Calling error_cb at %r.', |
43 | f.__name__, self.app_name, self.error_cb) |
44 | - self.error_cb(self.app_name, msg, traceback.format_exc()) |
45 | + error_dict = {ERROR_KEY: msg, |
46 | + ERROR_DETAIL_KEY: traceback.format_exc()} |
47 | + self.error_cb(self.app_name, error_dict) |
48 | return result |
49 | |
50 | return inner |
51 | @@ -173,7 +187,7 @@ |
52 | """Handle UI error when login/registration failed.""" |
53 | logger.warning('Login/registration failed app %r, error %r', |
54 | app_name, error) |
55 | - self.error_cb(app_name, error, 'No detailed error from the GUI.') |
56 | + self.error_cb(app_name, {ERROR_KEY: error}) |
57 | |
58 | def _auth_denial_cb(self, dialog, app_name): |
59 | """The user decided not to allow the registration or login.""" |
60 | |
61 | === modified file 'ubuntu_sso/main.py' |
62 | --- ubuntu_sso/main.py 2010-10-04 18:05:06 +0000 |
63 | +++ ubuntu_sso/main.py 2010-10-07 12:44:44 +0000 |
64 | @@ -33,9 +33,12 @@ |
65 | |
66 | import dbus.service |
67 | |
68 | -from ubuntu_sso import DBUS_IFACE_USER_NAME, DBUS_IFACE_CRED_NAME |
69 | +from ubuntu_sso import (DBUS_IFACE_USER_NAME, DBUS_IFACE_CRED_NAME, |
70 | + DBUS_IFACE_CREDENTIALS_NAME) |
71 | from ubuntu_sso.account import Account |
72 | -from ubuntu_sso.credentials import Credentials, keyring_store_credentials |
73 | +from ubuntu_sso.credentials import (Credentials, keyring_store_credentials, |
74 | + HELP_TEXT_KEY, PING_URL_KEY, TC_URL_KEY, WINDOW_ID_KEY, |
75 | + SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY, ERROR_KEY, ERROR_DETAIL_KEY) |
76 | from ubuntu_sso.keyring import get_token_name, U1_APP_NAME |
77 | from ubuntu_sso.logger import setup_logging |
78 | |
79 | @@ -45,7 +48,7 @@ |
80 | |
81 | |
82 | logger = setup_logging("ubuntu_sso.main") |
83 | -PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/" |
84 | +U1_PING_URL = "https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/" |
85 | |
86 | |
87 | class SSOLoginProcessor(Account): |
88 | @@ -267,7 +270,13 @@ |
89 | |
90 | def __init__(self, *args, **kwargs): |
91 | dbus.service.Object.__init__(self, *args, **kwargs) |
92 | - self.ping_url = os.environ.get('USSOC_PING_URL', PING_URL) |
93 | + self.ping_url = os.environ.get('USSOC_PING_URL', U1_PING_URL) |
94 | + |
95 | + def _process_error(self, app_name, error_dict): |
96 | + """Process the 'error_dict' and emit CredentialsError.""" |
97 | + msg = error_dict.get(ERROR_KEY, 'No error message given.') |
98 | + detail = error_dict.get(ERROR_DETAIL_KEY, 'No detailed error given.') |
99 | + self.CredentialsError(app_name, msg, detail) |
100 | |
101 | @dbus.service.signal(DBUS_IFACE_CRED_NAME, signature="s") |
102 | def AuthorizationDenied(self, app_name): |
103 | @@ -317,7 +326,7 @@ |
104 | tc_url=terms_and_conditions_url, |
105 | help_text=help_text, window_id=window_id, |
106 | success_cb=self.CredentialsFound, |
107 | - error_cb=self.CredentialsError, |
108 | + error_cb=self._process_error, |
109 | denial_cb=self.AuthorizationDenied) |
110 | obj.register() |
111 | |
112 | @@ -337,7 +346,7 @@ |
113 | obj = Credentials(app_name=app_name, ping_url=ping_url, tc_url=None, |
114 | help_text=help_text, window_id=window_id, |
115 | success_cb=self.CredentialsFound, |
116 | - error_cb=self.CredentialsError, |
117 | + error_cb=self._process_error, |
118 | denial_cb=self.AuthorizationDenied) |
119 | obj.login() |
120 | |
121 | @@ -350,3 +359,131 @@ |
122 | """ |
123 | obj = Credentials(app_name=app_name) |
124 | obj.clear_credentials() |
125 | + |
126 | + |
127 | +class CredentialsManagement(dbus.service.Object): |
128 | + """DBus object that manages credentials. |
129 | + |
130 | + Every exposed method in this class requires one mandatory argument: |
131 | + |
132 | + - 'app_name': the name of the application. Will be displayed in the |
133 | + GUI header, plus it will be used to find/build/clear tokens. |
134 | + |
135 | + And accepts another parameter named 'args', which is a dictionary that |
136 | + can contain the following: |
137 | + |
138 | + - 'help_text': an explanatory text for the end-users, will be |
139 | + shown below the header. This is an optional free text field. |
140 | + |
141 | + - 'ping_url': the url to open after successful token retrieval. If |
142 | + defined, the email will be attached to the url and will be pinged |
143 | + with a OAuth-signed request. |
144 | + |
145 | + - 'tc_url': the link to the Terms and Conditions page. If defined, |
146 | + the checkbox to agree to the terms will link to it. |
147 | + |
148 | + - 'window_id': the id of the window which will be set as a parent |
149 | + of the GUI. If not defined, no parent will be set. |
150 | + |
151 | + """ |
152 | + |
153 | + # Operator not preceded by a space (fails with dbus decorators) |
154 | + # pylint: disable=C0322 |
155 | + |
156 | + def _parse_args(self, args): |
157 | + """Retrieve values from the generic param 'args'.""" |
158 | + result = dict((k, v) for k, v in args.iteritems() if k in |
159 | + (HELP_TEXT_KEY, PING_URL_KEY, TC_URL_KEY, WINDOW_ID_KEY)) |
160 | + result[WINDOW_ID_KEY] = int(args.get(WINDOW_ID_KEY, 0)) |
161 | + result[SUCCESS_CB_KEY] = self.CredentialsFound |
162 | + result[ERROR_CB_KEY] = self.CredentialsError |
163 | + result[DENIAL_CB_KEY] = self.AuthorizationDenied |
164 | + return result |
165 | + |
166 | + @dbus.service.signal(DBUS_IFACE_CREDENTIALS_NAME, signature='s') |
167 | + def AuthorizationDenied(self, app_name): |
168 | + """Signal thrown when the user denies the authorization.""" |
169 | + logger.info('%s: emitting AuthorizationDenied with app_name "%s".', |
170 | + self.__class__.__name__, app_name) |
171 | + |
172 | + @dbus.service.signal(DBUS_IFACE_CREDENTIALS_NAME, signature='sa{ss}') |
173 | + def CredentialsFound(self, app_name, credentials): |
174 | + """Signal thrown when the credentials are found.""" |
175 | + logger.info('%s: emitting CredentialsFound with app_name "%s".', |
176 | + self.__class__.__name__, app_name) |
177 | + |
178 | + @dbus.service.signal(DBUS_IFACE_CREDENTIALS_NAME, signature='s') |
179 | + def CredentialsNotFound(self, app_name): |
180 | + """Signal thrown when the credentials are not found.""" |
181 | + logger.info('%s: emitting CredentialsNotFound with app_name "%s".', |
182 | + self.__class__.__name__, app_name) |
183 | + |
184 | + @dbus.service.signal(DBUS_IFACE_CREDENTIALS_NAME, signature='s') |
185 | + def CredentialsCleared(self, app_name): |
186 | + """Signal thrown when the credentials were cleared.""" |
187 | + logger.info('%s: emitting CredentialsCleared with app_name "%s".', |
188 | + self.__class__.__name__, app_name) |
189 | + |
190 | + @dbus.service.signal(DBUS_IFACE_CREDENTIALS_NAME, signature='sa{ss}') |
191 | + def CredentialsError(self, app_name, error_dict): |
192 | + """Signal thrown when there is a problem getting the credentials.""" |
193 | + logger.error('%s: emitting CredentialsError with app_name "%s" and ' |
194 | + 'error_dict %r.', self.__class__.__name__, app_name, |
195 | + error_dict) |
196 | + |
197 | + @dbus.service.method(dbus_interface=DBUS_IFACE_CREDENTIALS_NAME, |
198 | + in_signature='sa{ss}', out_signature='') |
199 | + def find_credentials(self, app_name, args): |
200 | + """Look for the credentials for an application. |
201 | + |
202 | + - 'app_name': the name of the application which credentials are |
203 | + going to be removed. |
204 | + |
205 | + - 'args' is a dictionary, currently not used. |
206 | + |
207 | + """ |
208 | + obj = Credentials(app_name) |
209 | + |
210 | + def success_cb(app, token): |
211 | + """Find credentials and notify using signals.""" |
212 | + if token is not None and len(token) > 0: |
213 | + self.CredentialsFound(app, token) |
214 | + else: |
215 | + self.CredentialsNotFound(app) |
216 | + |
217 | + blocking(obj.find_credentials, app_name, |
218 | + success_cb, self.CredentialsError) |
219 | + |
220 | + @dbus.service.method(dbus_interface=DBUS_IFACE_CREDENTIALS_NAME, |
221 | + in_signature='sa{ss}', out_signature='') |
222 | + def clear_credentials(self, app_name, args): |
223 | + """Clear the token for an application. |
224 | + |
225 | + - 'app_name': the name of the application which credentials are |
226 | + going to be removed. |
227 | + |
228 | + - 'args' is a dictionary, currently not used. |
229 | + |
230 | + """ |
231 | + obj = Credentials(app_name) |
232 | + |
233 | + def success_cb(app_name, result): |
234 | + """Clear credentials and notify using signals.""" |
235 | + self.CredentialsCleared(app_name) |
236 | + |
237 | + blocking(obj.clear_credentials, app_name, |
238 | + success_cb, self.CredentialsError) |
239 | + |
240 | + @dbus.service.method(dbus_interface=DBUS_IFACE_CREDENTIALS_NAME, |
241 | + in_signature='sa{ss}', out_signature='') |
242 | + def register(self, app_name, args): |
243 | + """Get credentials if found else prompt GUI to register.""" |
244 | + obj = Credentials(app_name, **self._parse_args(args)) |
245 | + obj.register() |
246 | + |
247 | + @dbus.service.method(dbus_interface=DBUS_IFACE_CREDENTIALS_NAME, |
248 | + in_signature='sa{ss}', out_signature='') |
249 | + def login(self, app_name, args): |
250 | + """Get credentials if found else prompt GUI to login.""" |
251 | + obj = Credentials(app_name, **self._parse_args(args)) |
252 | + obj.login() |
253 | |
254 | === modified file 'ubuntu_sso/tests/__init__.py' |
255 | --- ubuntu_sso/tests/__init__.py 2010-10-01 15:21:25 +0000 |
256 | +++ ubuntu_sso/tests/__init__.py 2010-10-07 12:44:44 +0000 |
257 | @@ -20,7 +20,7 @@ |
258 | |
259 | from ubuntu_sso.keyring import get_token_name |
260 | |
261 | -APP_NAME = 'The Super testing app!' |
262 | +APP_NAME = 'The Super App!' |
263 | CAPTCHA_ID = 'test' |
264 | CAPTCHA_PATH = os.path.abspath(os.path.join(os.curdir, 'ubuntu_sso', 'tests', |
265 | 'files', 'captcha.png')) |
266 | |
267 | === modified file 'ubuntu_sso/tests/test_credentials.py' |
268 | --- ubuntu_sso/tests/test_credentials.py 2010-10-04 18:13:52 +0000 |
269 | +++ ubuntu_sso/tests/test_credentials.py 2010-10-07 12:44:44 +0000 |
270 | @@ -25,8 +25,9 @@ |
271 | from twisted.trial.unittest import TestCase, FailTest |
272 | |
273 | from contrib.testing.testcase import MementoHandler |
274 | -from ubuntu_sso import (APP_NAME_KEY, HELP_TEXT_KEY, NO_OP, |
275 | - PING_URL_KEY, TC_URL_KEY, WINDOW_ID_KEY, credentials, gui) |
276 | +from ubuntu_sso import credentials, gui |
277 | +from ubuntu_sso.credentials import (APP_NAME_KEY, HELP_TEXT_KEY, NO_OP, |
278 | + PING_URL_KEY, TC_URL_KEY, WINDOW_ID_KEY, ERROR_KEY, ERROR_DETAIL_KEY) |
279 | from ubuntu_sso.tests import (APP_NAME, EMAIL, HELP_TEXT, PING_URL, TC_URL, |
280 | TOKEN, WINDOW_ID) |
281 | |
282 | @@ -113,13 +114,17 @@ |
283 | """To be called on credentials denial.""" |
284 | self._set_called('denial', *args, **kwargs) |
285 | |
286 | - def assert_error_cb_called(self, msg, detailed_error): |
287 | + def assert_error_cb_called(self, msg, detailed_error=None): |
288 | """Check that self.error_cb was called with proper arguments.""" |
289 | self.assertEqual(len(self._called), 2) |
290 | self.assertEqual(self._called[0][0], 'error') |
291 | self.assertEqual(self._called[0][1], APP_NAME) |
292 | - self.assertEqual(self._called[0][2], msg) |
293 | - self.assertIn(str(detailed_error), self._called[0][3]) |
294 | + error_dict = self._called[0][2] |
295 | + self.assertEqual(error_dict[ERROR_KEY], msg) |
296 | + if detailed_error is not None: |
297 | + self.assertIn(str(detailed_error), error_dict[ERROR_DETAIL_KEY]) |
298 | + else: |
299 | + self.assertNotIn(ERROR_DETAIL_KEY, error_dict) |
300 | self.assertEqual(self._called[1], {}) |
301 | |
302 | |
303 | @@ -245,8 +250,7 @@ |
304 | def test_login_error_cb(self): |
305 | """On login/register error, self.error_cb is called.""" |
306 | self.obj._login_error_cb(dialog=None, app_name=APP_NAME, error='Bla') |
307 | - msg = 'No detailed error from the GUI.' |
308 | - self.assert_error_cb_called(msg='Bla', detailed_error=msg) |
309 | + self.assert_error_cb_called(msg='Bla') |
310 | |
311 | def test_auth_denial_cb(self): |
312 | """On auth denied, self.denial_cb is called.""" |
313 | |
314 | === modified file 'ubuntu_sso/tests/test_main.py' |
315 | --- ubuntu_sso/tests/test_main.py 2010-10-01 18:13:41 +0000 |
316 | +++ ubuntu_sso/tests/test_main.py 2010-10-07 12:44:44 +0000 |
317 | @@ -20,6 +20,7 @@ |
318 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
319 | """Tests for the main SSO client code.""" |
320 | |
321 | +import logging |
322 | import os |
323 | |
324 | from mocker import Mocker, MockerTestCase, ARGS, KWARGS |
325 | @@ -29,13 +30,16 @@ |
326 | import ubuntu_sso.keyring |
327 | import ubuntu_sso.main |
328 | |
329 | -from ubuntu_sso import credentials |
330 | +from contrib.testing.testcase import MementoHandler |
331 | +from ubuntu_sso import credentials, DBUS_IFACE_CREDENTIALS_NAME |
332 | from ubuntu_sso.keyring import U1_APP_NAME |
333 | -from ubuntu_sso.main import (blocking, except_to_errdict, |
334 | - keyring_store_credentials, PING_URL, SSOCredentials, SSOLogin) |
335 | +from ubuntu_sso.main import (U1_PING_URL, blocking, except_to_errdict, |
336 | + CredentialsManagement, keyring_store_credentials, SSOCredentials, SSOLogin) |
337 | +from ubuntu_sso.main import (HELP_TEXT_KEY, PING_URL_KEY, |
338 | + TC_URL_KEY, WINDOW_ID_KEY, SUCCESS_CB_KEY, ERROR_CB_KEY, DENIAL_CB_KEY) |
339 | from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID, |
340 | - CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, PASSWORD, TOKEN, TOKEN_NAME, |
341 | - WINDOW_ID) |
342 | + CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, PASSWORD, PING_URL, TOKEN, |
343 | + TOKEN_NAME, WINDOW_ID) |
344 | |
345 | |
346 | # Access to a protected member 'yyy' of a client class |
347 | @@ -46,12 +50,26 @@ |
348 | """The exception that will be thrown by the fake blocking.""" |
349 | |
350 | |
351 | +def fake_ok_blocking(f, app, cb, eb): |
352 | + """A fake blocking function that succeeds.""" |
353 | + cb(app, f()) |
354 | + |
355 | + |
356 | +def fake_err_blocking(f, app, cb, eb): |
357 | + """A fake blocking function that fails.""" |
358 | + try: |
359 | + f() |
360 | + except Exception, e: # pylint: disable=W0703 |
361 | + eb(app, except_to_errdict(e)) |
362 | + else: |
363 | + eb(app, except_to_errdict(BlockingSampleException())) |
364 | + |
365 | + |
366 | class SsoDbusTestCase(TestCase): |
367 | """Test the SSOLogin DBus interface.""" |
368 | |
369 | def setUp(self): |
370 | """Create the mocking bus.""" |
371 | - self.real_blocking = ubuntu_sso.main.blocking |
372 | self.mocker = Mocker() |
373 | self.mockbusname = self.mocker.mock() |
374 | mockbus = self.mocker.mock() |
375 | @@ -76,19 +94,6 @@ |
376 | self.mocker.verify() |
377 | self.mocker.restore() |
378 | |
379 | - def fake_ok_blocking(self, f, app, cb, eb): |
380 | - """A fake blocking function that succeeds.""" |
381 | - cb(app, f()) |
382 | - |
383 | - def fake_err_blocking(self, f, app, cb, eb): |
384 | - """A fake blocking function that fails.""" |
385 | - try: |
386 | - f() |
387 | - except Exception, e: # pylint: disable=W0703 |
388 | - eb(app, except_to_errdict(e)) |
389 | - else: |
390 | - eb(app, except_to_errdict(BlockingSampleException())) |
391 | - |
392 | def test_creation(self): |
393 | """Test that the object creation is successful.""" |
394 | self.mocker.replay() |
395 | @@ -109,7 +114,7 @@ |
396 | expected_result = "expected result" |
397 | self.create_mock_processor().generate_captcha(filename) |
398 | self.mocker.result(expected_result) |
399 | - self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) |
400 | + self.patch(ubuntu_sso.main, "blocking", fake_ok_blocking) |
401 | self.mocker.replay() |
402 | |
403 | def verify(app_name, result): |
404 | @@ -132,7 +137,7 @@ |
405 | expected_result = "expected result" |
406 | self.create_mock_processor().generate_captcha(filename) |
407 | self.mocker.result(expected_result) |
408 | - self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
409 | + self.patch(ubuntu_sso.main, "blocking", fake_err_blocking) |
410 | self.mocker.replay() |
411 | |
412 | def verify(app_name, errdict): |
413 | @@ -155,7 +160,7 @@ |
414 | self.create_mock_processor().register_user(EMAIL, PASSWORD, CAPTCHA_ID, |
415 | CAPTCHA_SOLUTION) |
416 | self.mocker.result(expected_result) |
417 | - self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) |
418 | + self.patch(ubuntu_sso.main, "blocking", fake_ok_blocking) |
419 | self.mocker.replay() |
420 | |
421 | def verify(app_name, result): |
422 | @@ -179,7 +184,7 @@ |
423 | self.create_mock_processor().register_user(EMAIL, PASSWORD, CAPTCHA_ID, |
424 | CAPTCHA_SOLUTION) |
425 | self.mocker.result(expected_result) |
426 | - self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
427 | + self.patch(ubuntu_sso.main, "blocking", fake_err_blocking) |
428 | self.mocker.replay() |
429 | |
430 | def verify(app_name, errdict): |
431 | @@ -201,7 +206,7 @@ |
432 | d = Deferred() |
433 | self.create_mock_processor().login(EMAIL, PASSWORD, TOKEN_NAME) |
434 | self.mocker.result(TOKEN) |
435 | - self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) |
436 | + self.patch(ubuntu_sso.main, "blocking", fake_ok_blocking) |
437 | self.mocker.replay() |
438 | |
439 | def verify(app_name, result): |
440 | @@ -222,7 +227,7 @@ |
441 | """Test that the login method fails as expected.""" |
442 | d = Deferred() |
443 | self.mockprocessorclass = self.mocker.mock() |
444 | - self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
445 | + self.patch(ubuntu_sso.main, "blocking", fake_err_blocking) |
446 | |
447 | def fake_gtn(*args): |
448 | """A fake get_token_name that fails.""" |
449 | @@ -251,7 +256,7 @@ |
450 | self.create_mock_processor().validate_email(EMAIL, PASSWORD, |
451 | EMAIL_TOKEN, TOKEN_NAME) |
452 | self.mocker.result(TOKEN) |
453 | - self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) |
454 | + self.patch(ubuntu_sso.main, "blocking", fake_ok_blocking) |
455 | self.mocker.replay() |
456 | |
457 | def verify(app_name, result): |
458 | @@ -272,7 +277,7 @@ |
459 | """Test that the validate_email method fails as expected.""" |
460 | d = Deferred() |
461 | self.mockprocessorclass = self.mocker.mock() |
462 | - self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
463 | + self.patch(ubuntu_sso.main, "blocking", fake_err_blocking) |
464 | |
465 | def fake_gtn(*args): |
466 | """A fake get_token_name that fails.""" |
467 | @@ -300,7 +305,7 @@ |
468 | d = Deferred() |
469 | processor = self.create_mock_processor() |
470 | processor.request_password_reset_token(EMAIL) |
471 | - self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) |
472 | + self.patch(ubuntu_sso.main, "blocking", fake_ok_blocking) |
473 | self.mocker.result(EMAIL) |
474 | self.mocker.replay() |
475 | |
476 | @@ -323,7 +328,7 @@ |
477 | |
478 | self.create_mock_processor().request_password_reset_token(EMAIL) |
479 | self.mocker.result(EMAIL) |
480 | - self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
481 | + self.patch(ubuntu_sso.main, "blocking", fake_err_blocking) |
482 | self.mocker.replay() |
483 | |
484 | def verify(app_name, errdict): |
485 | @@ -345,7 +350,7 @@ |
486 | self.create_mock_processor().set_new_password(EMAIL, EMAIL_TOKEN, |
487 | PASSWORD) |
488 | self.mocker.result(EMAIL) |
489 | - self.patch(ubuntu_sso.main, "blocking", self.fake_ok_blocking) |
490 | + self.patch(ubuntu_sso.main, "blocking", fake_ok_blocking) |
491 | self.mocker.replay() |
492 | |
493 | def verify(app_name, result): |
494 | @@ -369,7 +374,7 @@ |
495 | self.create_mock_processor().set_new_password(EMAIL, EMAIL_TOKEN, |
496 | PASSWORD) |
497 | self.mocker.result(expected_result) |
498 | - self.patch(ubuntu_sso.main, "blocking", self.fake_err_blocking) |
499 | + self.patch(ubuntu_sso.main, "blocking", fake_err_blocking) |
500 | self.mocker.replay() |
501 | |
502 | def verify(app_name, errdict): |
503 | @@ -576,15 +581,17 @@ |
504 | def setUp(self): |
505 | MockerTestCase.setUp(self) |
506 | self.client = SSOCredentials(self.mocker.mock()) |
507 | + self.args = {PING_URL_KEY: self.ping_url, |
508 | + TC_URL_KEY: TC_URL, HELP_TEXT_KEY: HELP_TEXT, |
509 | + WINDOW_ID_KEY: WINDOW_ID, |
510 | + SUCCESS_CB_KEY: self.client.CredentialsFound, |
511 | + ERROR_CB_KEY: self.client._process_error, |
512 | + DENIAL_CB_KEY: self.client.AuthorizationDenied} |
513 | |
514 | def test_login_or_register(self): |
515 | """login_or_register is correct.""" |
516 | mock_class = self.mocker.replace("ubuntu_sso.credentials.Credentials") |
517 | - mock_class(app_name=self.app_name, ping_url=self.ping_url, |
518 | - tc_url=TC_URL, help_text=HELP_TEXT, window_id=WINDOW_ID, |
519 | - success_cb=self.client.CredentialsFound, |
520 | - error_cb=self.client.CredentialsError, |
521 | - denial_cb=self.client.AuthorizationDenied) |
522 | + mock_class(app_name=self.app_name, **self.args) |
523 | creds_obj = self.mocker.mock() |
524 | self.mocker.result(creds_obj) |
525 | |
526 | @@ -596,12 +603,9 @@ |
527 | |
528 | def test_login_only(self): |
529 | """login_or_register is correct.""" |
530 | + self.args[TC_URL_KEY] = None |
531 | mock_class = self.mocker.replace("ubuntu_sso.credentials.Credentials") |
532 | - mock_class(app_name=self.app_name, ping_url=self.ping_url, |
533 | - tc_url=None, help_text=HELP_TEXT, window_id=WINDOW_ID, |
534 | - success_cb=self.client.CredentialsFound, |
535 | - error_cb=self.client.CredentialsError, |
536 | - denial_cb=self.client.AuthorizationDenied) |
537 | + mock_class(app_name=self.app_name, **self.args) |
538 | creds_obj = self.mocker.mock() |
539 | self.mocker.result(creds_obj) |
540 | |
541 | @@ -620,7 +624,7 @@ |
542 | """ |
543 | |
544 | app_name = U1_APP_NAME |
545 | - ping_url = PING_URL |
546 | + ping_url = U1_PING_URL |
547 | |
548 | |
549 | class ApplicationCredentialsClearTokenTestCase(TestCase, MockerTestCase): |
550 | @@ -660,4 +664,246 @@ |
551 | def test_no_override_ping_url(self): |
552 | """If the environ is unset, the default ping url is used.""" |
553 | creds = SSOCredentials(None) |
554 | - self.assertEqual(creds.ping_url, PING_URL) |
555 | + self.assertEqual(creds.ping_url, U1_PING_URL) |
556 | + |
557 | + |
558 | +class CredentialsManagementTestCase(TestCase): |
559 | + """Tests for the CredentialsManagement DBus interface.""" |
560 | + |
561 | + base_args = {HELP_TEXT_KEY: HELP_TEXT, PING_URL_KEY: PING_URL, |
562 | + TC_URL_KEY: TC_URL, WINDOW_ID_KEY: WINDOW_ID} |
563 | + |
564 | + def setUp(self): |
565 | + self.mocker = Mocker() |
566 | + self.client = CredentialsManagement() |
567 | + self.args = {} |
568 | + self.cred_args = {} |
569 | + |
570 | + def tearDown(self): |
571 | + """Verify the mocking stuff and shut it down.""" |
572 | + self.mocker.verify() |
573 | + self.mocker.restore() |
574 | + |
575 | + def assert_dbus_method_correct(self, method): |
576 | + """Check that 'method' is a dbus method with proper signatures.""" |
577 | + self.assertTrue(method._dbus_is_method) |
578 | + self.assertEqual(method._dbus_interface, DBUS_IFACE_CREDENTIALS_NAME) |
579 | + self.assertEqual(method._dbus_in_signature, 'sa{ss}') |
580 | + self.assertEqual(method._dbus_out_signature, '') |
581 | + |
582 | + def create_mock_backend(self): |
583 | + """Create a mock backend.""" |
584 | + mock_class = self.mocker.replace("ubuntu_sso.credentials.Credentials") |
585 | + mock_class(APP_NAME, **self.cred_args) |
586 | + creds_obj = self.mocker.mock() |
587 | + self.mocker.result(creds_obj) |
588 | + |
589 | + return creds_obj |
590 | + |
591 | + def test_is_dbus_object(self): |
592 | + """CredentialsManagement is a Dbus object.""" |
593 | + self.assertIsInstance(self.client, ubuntu_sso.main.dbus.service.Object) |
594 | + |
595 | + |
596 | +class CredentialsManagementFindClearTestCase(CredentialsManagementTestCase): |
597 | + """Tests for the CredentialsManagement find/clear methods.""" |
598 | + |
599 | + timeout = 5 |
600 | + |
601 | + def test_find_credentials(self): |
602 | + """The credentials are asked and returned in signals.""" |
603 | + self.create_mock_backend().find_credentials() |
604 | + self.mocker.replay() |
605 | + |
606 | + self.client.find_credentials(APP_NAME, self.args) |
607 | + self.assert_dbus_method_correct(self.client.find_credentials) |
608 | + |
609 | + def test_find_credentials_does_not_block_when_found(self): |
610 | + """Calling find_credentials does not block but return thru signals. |
611 | + |
612 | + If the creds are found, CredentialsFound is emitted. |
613 | + |
614 | + """ |
615 | + d = Deferred() |
616 | + |
617 | + def verify(app_name, creds): |
618 | + """The actual test.""" |
619 | + try: |
620 | + self.assertEqual(app_name, APP_NAME) |
621 | + self.assertEqual(creds, TOKEN) |
622 | + except Exception, e: # pylint: disable=W0703 |
623 | + d.errback(e) |
624 | + else: |
625 | + d.callback(creds) |
626 | + |
627 | + self.patch(ubuntu_sso.main, 'blocking', fake_ok_blocking) |
628 | + self.patch(self.client, 'CredentialsFound', verify) |
629 | + self.patch(self.client, 'CredentialsNotFound', d.errback) |
630 | + |
631 | + self.create_mock_backend().find_credentials() |
632 | + self.mocker.result(TOKEN) |
633 | + self.mocker.replay() |
634 | + |
635 | + self.client.find_credentials(APP_NAME, self.args) |
636 | + return d |
637 | + |
638 | + def test_find_credentials_does_not_block_when_not_found(self): |
639 | + """Calling find_credentials does not block but return thru signals. |
640 | + |
641 | + If the creds are not found, CredentialsNotFound is emitted. |
642 | + |
643 | + """ |
644 | + d = Deferred() |
645 | + |
646 | + def verify(app_name): |
647 | + """The actual test.""" |
648 | + try: |
649 | + self.assertEqual(app_name, APP_NAME) |
650 | + except Exception, e: # pylint: disable=W0703 |
651 | + d.errback(e) |
652 | + else: |
653 | + d.callback(app_name) |
654 | + |
655 | + self.patch(ubuntu_sso.main, 'blocking', fake_ok_blocking) |
656 | + self.patch(self.client, 'CredentialsFound', d.errback) |
657 | + self.patch(self.client, 'CredentialsNotFound', verify) |
658 | + |
659 | + self.create_mock_backend().find_credentials() |
660 | + self.mocker.result({}) |
661 | + self.mocker.replay() |
662 | + |
663 | + self.client.find_credentials(APP_NAME, self.args) |
664 | + return d |
665 | + |
666 | + def test_clear_credentials(self): |
667 | + """The credentials are removed.""" |
668 | + self.create_mock_backend().clear_credentials() |
669 | + self.mocker.replay() |
670 | + |
671 | + self.client.clear_credentials(APP_NAME, self.args) |
672 | + self.assert_dbus_method_correct(self.client.clear_credentials) |
673 | + |
674 | + def test_clear_credentials_does_not_block(self): |
675 | + """Calling clear_credentials does not block but return thru signals.""" |
676 | + d = Deferred() |
677 | + |
678 | + def verify(app_name): |
679 | + """The actual test.""" |
680 | + try: |
681 | + self.assertEqual(app_name, APP_NAME) |
682 | + except Exception, e: # pylint: disable=W0703 |
683 | + d.errback(e) |
684 | + else: |
685 | + d.callback(app_name) |
686 | + |
687 | + self.patch(ubuntu_sso.main, 'blocking', fake_ok_blocking) |
688 | + self.patch(self.client, 'CredentialsCleared', verify) |
689 | + |
690 | + self.create_mock_backend().clear_credentials() |
691 | + self.mocker.replay() |
692 | + |
693 | + self.client.clear_credentials(APP_NAME, self.args) |
694 | + return d |
695 | + |
696 | + |
697 | +class CredentialsManagementOpsTestCase(CredentialsManagementTestCase): |
698 | + """Tests for the CredentialsManagement login/register methods.""" |
699 | + |
700 | + def setUp(self): |
701 | + super(CredentialsManagementOpsTestCase, self).setUp() |
702 | + self.args = dict((k, str(v)) for k, v in self.base_args.iteritems()) |
703 | + self.cred_args = self.base_args.copy() |
704 | + self.cred_args[SUCCESS_CB_KEY] = self.client.CredentialsFound |
705 | + self.cred_args[ERROR_CB_KEY] = self.client.CredentialsError |
706 | + self.cred_args[DENIAL_CB_KEY] = self.client.AuthorizationDenied |
707 | + |
708 | + def test_register(self): |
709 | + """The registration is correct.""" |
710 | + self.create_mock_backend().register() |
711 | + self.mocker.replay() |
712 | + |
713 | + self.client.register(APP_NAME, self.args) |
714 | + self.assert_dbus_method_correct(self.client.register) |
715 | + |
716 | + def test_login(self): |
717 | + """The login is correct.""" |
718 | + self.create_mock_backend().login() |
719 | + self.mocker.replay() |
720 | + |
721 | + self.client.login(APP_NAME, self.args) |
722 | + self.assert_dbus_method_correct(self.client.login) |
723 | + |
724 | + |
725 | +class CredentialsManagementParamsTestCase(CredentialsManagementOpsTestCase): |
726 | + """Tests for the CredentialsManagement extra parameters handling.""" |
727 | + |
728 | + def setUp(self): |
729 | + super(CredentialsManagementParamsTestCase, self).setUp() |
730 | + self.args['dummy'] = 'nothing useful' |
731 | + |
732 | + |
733 | +class CredentialsManagementSignalsTestCase(TestCase): |
734 | + """Tests for the CredentialsManagement DBus signals.""" |
735 | + |
736 | + def setUp(self): |
737 | + self.client = CredentialsManagement() |
738 | + |
739 | + self.memento = MementoHandler() |
740 | + self.memento.setLevel(logging.DEBUG) |
741 | + ubuntu_sso.main.logger.addHandler(self.memento) |
742 | + |
743 | + def assert_dbus_signal_correct(self, signal, signature): |
744 | + """Check that 'signal' is a dbus signal with proper 'signature'.""" |
745 | + self.assertTrue(signal._dbus_is_signal) |
746 | + self.assertEqual(signal._dbus_interface, DBUS_IFACE_CREDENTIALS_NAME) |
747 | + self.assertEqual(signal._dbus_signature, signature) |
748 | + |
749 | + def test_credentials_found(self): |
750 | + """The CredentialsFound signal.""" |
751 | + self.client.CredentialsFound(APP_NAME, TOKEN) |
752 | + msgs = (self.client.__class__.__name__, |
753 | + self.client.CredentialsFound.__name__, APP_NAME) |
754 | + self.assertTrue(self.memento.check_info(*msgs)) |
755 | + |
756 | + msg = 'credentials must not be logged (found %r in log).' |
757 | + for val in TOKEN.itervalues(): |
758 | + self.assertFalse(self.memento.check_info(val), msg % val) |
759 | + |
760 | + self.assert_dbus_signal_correct(self.client.CredentialsFound, 'sa{ss}') |
761 | + |
762 | + def test_credentials_not_found(self): |
763 | + """The CredentialsNotFound signal.""" |
764 | + self.client.CredentialsNotFound(APP_NAME) |
765 | + msgs = (self.client.__class__.__name__, |
766 | + self.client.CredentialsNotFound.__name__, APP_NAME) |
767 | + self.assertTrue(self.memento.check_info(*msgs)) |
768 | + self.assert_dbus_signal_correct(self.client.CredentialsNotFound, 's') |
769 | + |
770 | + def test_credentials_cleared(self): |
771 | + """The CredentialsCleared signal.""" |
772 | + self.client.CredentialsCleared(APP_NAME) |
773 | + msgs = (self.client.__class__.__name__, |
774 | + self.client.CredentialsCleared.__name__, APP_NAME) |
775 | + self.assertTrue(self.memento.check_info(*msgs)) |
776 | + |
777 | + self.assert_dbus_signal_correct(self.client.CredentialsCleared, 's') |
778 | + |
779 | + def test_credentials_error(self): |
780 | + """The CredentialsError signal.""" |
781 | + error = {'error_message': 'failed!', 'detailed error': 'yadda yadda'} |
782 | + self.client.CredentialsError(APP_NAME, error) |
783 | + msgs = (self.client.__class__.__name__, |
784 | + self.client.CredentialsError.__name__, |
785 | + APP_NAME, str(error)) |
786 | + self.assertTrue(self.memento.check_error(*msgs)) |
787 | + |
788 | + self.assert_dbus_signal_correct(self.client.CredentialsError, 'sa{ss}') |
789 | + |
790 | + def test_authorization_denied(self): |
791 | + """The AuthorizationDenied signal.""" |
792 | + self.client.AuthorizationDenied(APP_NAME) |
793 | + msgs = (self.client.__class__.__name__, |
794 | + self.client.AuthorizationDenied.__name__, APP_NAME) |
795 | + self.assertTrue(self.memento.check_info(*msgs)) |
796 | + |
797 | + self.assert_dbus_signal_correct(self.client.AuthorizationDenied, 's') |
Looks fine!
The documentation at https:/ /wiki.ubuntu. com/SingleSignO n/UbuntuSsoClie nt should be updated as well. UbuntuSsoClient /BetaInterface while Natty is in development, then move it as the main page and move the current to /SingleSignOn/ UbuntuSsoClient /1.0-Deprecated
Perhaps have a /SingleSignOn/