Merge lp:~mandel/ubuntu-sso-client/implement_windows_main_3 into lp:ubuntu-sso-client

Proposed by Manuel de la Peña
Status: Merged
Approved by: dobey
Approved revision: 696
Merged at revision: 686
Proposed branch: lp:~mandel/ubuntu-sso-client/implement_windows_main_3
Merge into: lp:ubuntu-sso-client
Prerequisite: lp:~mandel/ubuntu-sso-client/implement_windows_main_2
Diff against target: 472 lines (+416/-5)
2 files modified
ubuntu_sso/main/tests/test_windows.py (+257/-1)
ubuntu_sso/main/windows.py (+159/-4)
To merge this branch: bzr merge lp:~mandel/ubuntu-sso-client/implement_windows_main_3
Reviewer Review Type Date Requested Status
Shane Fagan (community) Approve
Natalia Bidart (community) Approve
Review via email: mp+53409@code.launchpad.net

Commit message

Part of fixing for LP: #728339:

- Provide the implementation of the server and client side of the SSOCredentials API so that the IPC on windows can use twisted.pb.

Description of the change

Third step of the implementation of main on windows.

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks good!

review: Approve
Revision history for this message
Shane Fagan (shanepatrickfagan) :
review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

Attempt to merge into lp:ubuntu-sso-client failed due to conflicts:

text conflict in ubuntu_sso/main/tests/test_windows.py

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/main/tests/test_windows.py'
2--- ubuntu_sso/main/tests/test_windows.py 2011-03-24 17:07:57 +0000
3+++ ubuntu_sso/main/tests/test_windows.py 2011-03-24 20:08:39 +0000
4@@ -16,12 +16,15 @@
5 # with this program. If not, see <http://www.gnu.org/licenses/>.
6 """Windows tests."""
7
8-from mocker import Mocker
9+from mocker import MATCH, Mocker
10+
11 from twisted.internet import defer, reactor
12 from twisted.trial.unittest import TestCase
13 from twisted.spread.pb import PBClientFactory, PBServerFactory
14 from ubuntu_sso.main.windows import (
15 blocking,
16+ SSOCredentials,
17+ SSOCredentialsClient,
18 SSOLogin,
19 SSOLoginClient,
20 UbuntuSSORoot)
21@@ -518,3 +521,256 @@
22 d.addCallback(test_execution)
23 # pylint: enable=E1101
24 return d
25+
26+
27+class SSOCredentialsTestCase(TestCase):
28+ """Test the credentials class."""
29+
30+ def setUp(self):
31+ """Set up tests."""
32+ super(SSOCredentialsTestCase, self).setUp()
33+ self.mocker = Mocker()
34+ self.root = self.mocker.mock()
35+ self.except_to_errdict = self.mocker.replace(
36+ 'ubuntu_sso.main.except_to_errdict')
37+ self.creds = SSOCredentials(None)
38+ self.creds.root = self.root
39+ # start pb
40+ self.sso_root = UbuntuSSORoot(None, self.creds, None)
41+ self.server_factory = SaveProtocolServerFactory(self.sso_root)
42+ # pylint: disable=E1101
43+ self.listener = reactor.listenTCP(0, self.server_factory)
44+ self.client_factory = PBClientFactory()
45+ self.connector = reactor.connectTCP('localhost',
46+ self.listener.getHost().port,
47+ self.client_factory)
48+ # pylint: enable=E1101
49+
50+ def tearDown(self):
51+ """Clean reactor."""
52+ if self.server_factory.protocolInstance is not None:
53+ self.server_factory.protocolInstance.transport.loseConnection()
54+ return defer.gatherResults([self._tearDownServer(),
55+ self._tearDownClient()])
56+
57+ def _tearDownServer(self):
58+ """Teardown the server."""
59+ return defer.maybeDeferred(self.listener.stopListening)
60+
61+ def _tearDownClient(self):
62+ """Tear down the client."""
63+ self.connector.disconnect()
64+ return defer.succeed(None)
65+
66+ @defer.inlineCallbacks
67+ def _get_client(self):
68+ """Get the client."""
69+ # request the remote objct and create a client
70+ root = yield self.client_factory.getRootObject()
71+ remote = yield root.callRemote('get_sso_credentials')
72+ client = SSOCredentialsClient(remote)
73+ yield client.register_to_signals()
74+ # set the cb
75+ for signal in ['on_authorization_denied_cb',
76+ 'on_credentials_found_cb',
77+ 'on_credentials_error_cb']:
78+ setattr(client, signal, self.mocker.mock())
79+ defer.returnValue(client)
80+
81+ def test_emit_authorization_denied(self):
82+ """Test that the cb is executed."""
83+ app_name = 'app'
84+
85+ @defer.inlineCallbacks
86+ def test_emit(client):
87+ """Actual test."""
88+ client.on_authorization_denied_cb(app_name)
89+ self.mocker.replay()
90+ self.creds.emit_authorization_denied(app_name)
91+ yield client.unregister_to_signals()
92+ self.mocker.verify()
93+
94+ d = self._get_client()
95+ # pylint: disable=E1101
96+ d.addCallback(test_emit)
97+ # pylint: enable=E1101
98+ return d
99+
100+ def test_emit_credentials_found(self):
101+ """Test that the cb was executed."""
102+ app_name = 'app'
103+ credentials = 'cred'
104+
105+ @defer.inlineCallbacks
106+ def test_emit(client):
107+ """Actual test."""
108+ client.on_credentials_found_cb(app_name, credentials)
109+ self.mocker.replay()
110+ self.creds.emit_credentials_found(app_name, credentials)
111+ yield client.unregister_to_signals()
112+ self.mocker.verify()
113+
114+ d = self._get_client()
115+ # pylint: disable=E1101
116+ d.addCallback(test_emit)
117+ # pylint: enable=E1101
118+ return d
119+
120+ def test_emit_credentials_error(self):
121+ """Test that the cb was executed."""
122+ app_name = 'app'
123+ error = 'error'
124+ detail = 'detail'
125+
126+ @defer.inlineCallbacks
127+ def test_emit(client):
128+ """Actual test."""
129+ client.on_credentials_error_cb(app_name, error, detail)
130+ self.mocker.replay()
131+ self.creds.emit_credentials_error(app_name, error, detail)
132+ yield client.unregister_to_signals()
133+ self.mocker.verify()
134+
135+ d = self._get_client()
136+ # pylint: disable=E1101
137+ d.addCallback(test_emit)
138+ # pylint: enable=E1101
139+ return d
140+
141+ def test_find_credentials_no_kword(self):
142+ """Ensure that the root is called."""
143+ app_name = 'app'
144+ callback = lambda: None
145+ errback = lambda: None
146+
147+ @defer.inlineCallbacks
148+ def test_execution(client):
149+ """Actual test."""
150+ self.root.find_credentials(app_name, MATCH(callable),
151+ MATCH(callable))
152+ self.mocker.replay()
153+ yield client.find_credentials(app_name, callback, errback)
154+ yield client.unregister_to_signals()
155+
156+ d = self._get_client()
157+ # pylint: disable=E1101
158+ d.addCallback(test_execution)
159+ # pylint: enable=E1101
160+ return d
161+
162+ def test_find_credentials_(self):
163+ """Ensure that the root is called."""
164+ app_name = 'app'
165+ callback = lambda: None
166+ errback = lambda: None
167+
168+ @defer.inlineCallbacks
169+ def test_execution(client):
170+ """Actual test."""
171+ self.root.find_credentials(app_name, MATCH(callable),
172+ MATCH(callable))
173+ self.mocker.replay()
174+ yield client.find_credentials(app_name, callback=callback,
175+ errback=errback)
176+ yield client.unregister_to_signals()
177+
178+ d = self._get_client()
179+ # pylint: disable=E1101
180+ d.addCallback(test_execution)
181+ # pylint: enable=E1101
182+ return d
183+
184+ def test_login_or_register_to_get_credentials(self):
185+ """Ensure that the root is called."""
186+ app_name = 'app'
187+ terms = 'terms'
188+ help_txt = 'help'
189+ window_id = 'window_id'
190+
191+ @defer.inlineCallbacks
192+ def test_execution(client):
193+ """Actual test."""
194+ # pylint: disable=W0212
195+ self.root.login_or_register_to_get_credentials(app_name, terms,
196+ help_txt, window_id,
197+ self.creds.emit_credentials_found,
198+ self.creds._process_error,
199+ self.creds.emit_authorization_denied)
200+ # pylint: enable=W0212
201+ self.mocker.replay()
202+ yield client.login_or_register_to_get_credentials(app_name, terms,
203+ help_txt, window_id)
204+ yield client.unregister_to_signals()
205+
206+ d = self._get_client()
207+ # pylint: disable=E1101
208+ d.addCallback(test_execution)
209+ # pylint: enable=E1101
210+ return d
211+
212+ def test_login_to_get_credentials(self):
213+ """Ensure that the root is called."""
214+ app_name = 'app'
215+ help_txt = 'help'
216+ window_id = 'window_id'
217+
218+ @defer.inlineCallbacks
219+ def test_execution(client):
220+ """Actual test."""
221+ # pylint: disable=W0212
222+ self.root.login_to_get_credentials(app_name, help_txt, window_id,
223+ self.creds.emit_credentials_found,
224+ self.creds._process_error,
225+ self.creds.emit_authorization_denied)
226+ # pylint: enable=W0212
227+ self.mocker.replay()
228+ yield client.login_to_get_credentials(app_name, help_txt,
229+ window_id)
230+ yield client.unregister_to_signals()
231+
232+ d = self._get_client()
233+ # pylint: disable=E1101
234+ d.addCallback(test_execution)
235+ # pylint: enable=E1101
236+ return d
237+
238+ def test_clear_token_no_kword(self):
239+ """Ensure that the root is called."""
240+ app_name = 'app'
241+ callback = lambda: None
242+ errback = lambda: None
243+
244+ @defer.inlineCallbacks
245+ def test_execution(client):
246+ """Actual test."""
247+ self.root.clear_token(app_name, MATCH(callable), MATCH(callable))
248+ self.mocker.replay()
249+ yield client.clear_token(app_name, callback, errback)
250+ yield client.unregister_to_signals()
251+
252+ d = self._get_client()
253+ # pylint: disable=E1101
254+ d.addCallback(test_execution)
255+ # pylint: enable=E1101
256+ return d
257+
258+ def test_clear_token(self):
259+ """Ensure that the root is called."""
260+ app_name = 'app'
261+ callback = lambda: None
262+ errback = lambda: None
263+
264+ @defer.inlineCallbacks
265+ def test_execution(client):
266+ """Actual test."""
267+ self.root.clear_token(app_name, MATCH(callable), MATCH(callable))
268+ self.mocker.replay()
269+ yield client.clear_token(app_name, callback=callback,
270+ errback=errback)
271+ yield client.unregister_to_signals()
272+
273+ d = self._get_client()
274+ # pylint: disable=E1101
275+ d.addCallback(test_execution)
276+ # pylint: enable=E1101
277+ return d
278
279=== modified file 'ubuntu_sso/main/windows.py'
280--- ubuntu_sso/main/windows.py 2011-03-24 17:07:57 +0000
281+++ ubuntu_sso/main/windows.py 2011-03-24 20:08:39 +0000
282@@ -26,9 +26,12 @@
283 from win32pipe import CallNamedPipe
284 # pylint: enable=F0401
285
286+from ubuntu_sso import NO_OP
287 from ubuntu_sso.account import Account
288+from ubuntu_sso.credentials import ERROR_KEY, ERROR_DETAIL_KEY
289 from ubuntu_sso.logger import setup_logging
290-from ubuntu_sso.main import SSOLoginRoot, except_to_errdict
291+from ubuntu_sso.main import (SSOLoginRoot, SSOCredentialsRoot,
292+ except_to_errdict)
293
294 logger = setup_logging("ubuntu_sso.main")
295 NAMED_PIPE_URL = '\\\\.\\pipe\\ubuntu_sso\\%s'
296@@ -251,11 +254,98 @@
297
298
299 class SSOCredentials(Referenceable, SignalBroadcaster):
300- """Ipc object that gets credentials, and login/registers if needed."""
301+ """DBus object that gets credentials, and login/registers if needed."""
302+
303+ __metaclass__ = RemoteMeta
304+
305+ # calls that will be accessible remotly
306+ remote_calls = [
307+ 'find_credentials',
308+ 'login_or_register_to_get_credentials',
309+ 'login_to_get_credentials',
310+ 'clear_token',
311+ ]
312+
313+ def __init__(self, *args, **kwargs):
314+ super(SSOCredentials, self).__init__()
315+ self.root = SSOCredentialsRoot()
316+
317+ def _process_error(self, app_name, error_dict):
318+ """Process the 'error_dict' and emit CredentialsError."""
319+ msg = error_dict.get(ERROR_KEY, 'No error message given.')
320+ detail = error_dict.get(ERROR_DETAIL_KEY, 'No detailed error given.')
321+ self.emit_credentials_error(app_name, msg, detail)
322+
323+ def emit_authorization_denied(self, app_name):
324+ """Signal thrown when the user denies the authorization."""
325+ logger.info('SSOCredentials: emitting AuthorizationDenied with '
326+ 'app_name "%s"', app_name)
327+ self.emit_signal('on_authorization_denied', app_name)
328+
329+ def emit_credentials_found(self, app_name, credentials):
330+ """Signal thrown when the credentials are found."""
331+ logger.info('SSOCredentials: emitting CredentialsFound with '
332+ 'app_name "%s"', app_name)
333+ self.emit_signal('on_credentials_found', app_name, credentials)
334+
335+ def emit_credentials_error(self, app_name, error_message, detailed_error):
336+ """Signal thrown when there is a problem finding the credentials."""
337+ logger.error('SSOCredentials: emitting CredentialsError with app_name '
338+ '"%s" and error_message %r', app_name, error_message)
339+ self.emit_signal('on_credentials_error', app_name, error_message,
340+ detailed_error)
341+
342+ def find_credentials(self, app_name, callback=NO_OP, errback=NO_OP):
343+ """Get the credentials from the keyring or {} if not there."""
344+ self.root.find_credentials(app_name, remote_handler(callback),
345+ remote_handler(errback))
346+
347+ def login_or_register_to_get_credentials(self, app_name,
348+ terms_and_conditions_url,
349+ help_text, window_id):
350+ """Get credentials if found else prompt GUI to login or register.
351+
352+ 'app_name' will be displayed in the GUI.
353+ 'terms_and_conditions_url' will be the URL pointing to T&C.
354+ 'help_text' is an explanatory text for the end-users, will be shown
355+ below the headers.
356+ 'window_id' is the id of the window which will be set as a parent of
357+ the GUI. If 0, no parent will be set.
358+
359+ """
360+ self.root.login_or_register_to_get_credentials(app_name,
361+ terms_and_conditions_url,
362+ help_text, window_id,
363+ self.emit_credentials_found,
364+ self._process_error,
365+ self.emit_authorization_denied)
366+
367+ def login_to_get_credentials(self, app_name, help_text, window_id):
368+ """Get credentials if found else prompt GUI just to login
369+
370+ 'app_name' will be displayed in the GUI.
371+ 'help_text' is an explanatory text for the end-users, will be shown
372+ before the login fields.
373+ 'window_id' is the id of the window which will be set as a parent of
374+ the GUI. If 0, no parent will be set.
375+
376+ """
377+ self.root.login_to_get_credentials(app_name, help_text, window_id,
378+ self.emit_credentials_found,
379+ self._process_error,
380+ self.emit_authorization_denied)
381+
382+ def clear_token(self, app_name, callback=NO_OP, errback=NO_OP):
383+ """Clear the token for an application from the keyring.
384+
385+ 'app_name' is the name of the application.
386+ """
387+ self.root.clear_token(app_name, remote_handler(callback),
388+ remote_handler(errback))
389
390
391 class CredentialsManagement(Referenceable, SignalBroadcaster):
392- """Object that manages credentials"""
393+ """Object that manages credentials."""
394
395
396 class UbuntuSSORoot(object, Root):
397@@ -475,9 +565,74 @@
398 class SSOCredentialsClient(RemoteClient, Referenceable):
399 """Client that can perform calls to the remote SSOCredentials object."""
400
401+ __metaclass__ = RemoteMeta
402+
403+ # calls that will be accessible remotly
404+ remote_calls = [
405+ 'on_authorization_denied',
406+ 'on_credentials_found',
407+ 'on_credentials_error',
408+ ]
409+
410+ def __init__(self, remote_login):
411+ """Create a client for the cred API."""
412+ super(SSOCredentialsClient, self).__init__(remote_login)
413+
414+ @signal
415+ def on_authorization_denied(self, app_name):
416+ """Signal thrown when the user denies the authorization."""
417+
418+ @signal
419+ def on_credentials_found(self, app_name, credentials):
420+ """Signal thrown when the credentials are found."""
421+
422+ @signal
423+ def on_credentials_error(self, app_name, error_message, detailed_error):
424+ """Signal thrown when there is a problem finding the credentials."""
425+
426+ @callbacks(callbacks_names=[('callback', 2), ('errback', 3)])
427+ @remote
428+ def find_credentials(self, app_name, callback=NO_OP, errback=NO_OP):
429+ """Get the credentials from the keyring or {} if not there."""
430+
431+ @remote
432+ def login_or_register_to_get_credentials(self, app_name,
433+ terms_and_conditions_url,
434+ help_text, window_id):
435+ """Get credentials if found else prompt GUI to login or register.
436+
437+ 'app_name' will be displayed in the GUI.
438+ 'terms_and_conditions_url' will be the URL pointing to T&C.
439+ 'help_text' is an explanatory text for the end-users, will be shown
440+ below the headers.
441+ 'window_id' is the id of the window which will be set as a parent of
442+ the GUI. If 0, no parent will be set.
443+
444+ """
445+
446+ @remote
447+ def login_to_get_credentials(self, app_name, help_text, window_id):
448+ """Get credentials if found else prompt GUI just to login
449+
450+ 'app_name' will be displayed in the GUI.
451+ 'help_text' is an explanatory text for the end-users, will be shown
452+ before the login fields.
453+ 'window_id' is the id of the window which will be set as a parent of
454+ the GUI. If 0, no parent will be set.
455+
456+ """
457+
458+ @callbacks(callbacks_names=[('callback', 2), ('errback', 3)])
459+ @remote
460+ def clear_token(self, app_name, callback=NO_OP, errback=NO_OP):
461+ """Clear the token for an application from the keyring.
462+
463+ 'app_name' is the name of the application.
464+ """
465+
466
467 class CredentialsManagementClient(RemoteClient, Referenceable):
468- """Client that can perform calls to the remote CredManagement object."""
469+ """Cleint that can perform calls to the remote CredManagement object."""
470
471
472 class UbuntuSSOClientException(Exception):

Subscribers

People subscribed via source and target branches