Merge lp:~nataliabidart/ubuntu-sso-client/new-dbus-iface into lp:ubuntu-sso-client

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
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
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Looks fine!

The documentation at https://wiki.ubuntu.com/SingleSignOn/UbuntuSsoClient should be updated as well.
Perhaps have a /SingleSignOn/UbuntuSsoClient/BetaInterface while Natty is in development, then move it as the main page and move the current to /SingleSignOn/UbuntuSsoClient/1.0-Deprecated

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')

Subscribers

People subscribed via source and target branches