Merge lp:~mandel/ubuntu-sso-client/implement_windows_main_3 into lp:ubuntu-sso-client
- implement_windows_main_3
- Merge into trunk
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 |
Related bugs: |
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
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_
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): |
Looks good!