Merge lp:~diegosarmentero/ubuntu-sso-client/mac-port into lp:ubuntu-sso-client

Proposed by Diego Sarmentero on 2012-04-25
Status: Merged
Approved by: Diego Sarmentero on 2012-05-01
Approved revision: 955
Merged at revision: 952
Proposed branch: lp:~diegosarmentero/ubuntu-sso-client/mac-port
Merge into: lp:ubuntu-sso-client
Diff against target: 1655 lines (+825/-464)
25 files modified
data/qt/darwin.qss (+5/-0)
data/qt/resources.qrc (+1/-0)
run-mac-tests (+56/-0)
run-tests (+1/-1)
ubuntu_sso/keyring/__init__.py (+2/-6)
ubuntu_sso/keyring/linux.py (+0/-6)
ubuntu_sso/keyring/pykeyring.py (+2/-3)
ubuntu_sso/keyring/tests/test_pykeyring.py (+8/-9)
ubuntu_sso/main/__init__.py (+27/-1)
ubuntu_sso/main/darwin.py (+110/-0)
ubuntu_sso/main/perspective_broker.py (+352/-0)
ubuntu_sso/main/tests/test_darwin.py (+43/-0)
ubuntu_sso/main/tests/test_windows.py (+3/-1)
ubuntu_sso/main/windows.py (+23/-335)
ubuntu_sso/networkstate/__init__.py (+11/-11)
ubuntu_sso/networkstate/darwin.py (+95/-0)
ubuntu_sso/networkstate/linux.py (+7/-39)
ubuntu_sso/networkstate/networkstates.py (+51/-0)
ubuntu_sso/networkstate/tests/test_linux.py (+9/-10)
ubuntu_sso/qt/__init__.py (+7/-0)
ubuntu_sso/qt/main/__init__.py (+1/-1)
ubuntu_sso/utils/__init__.py (+9/-6)
ubuntu_sso/utils/linux.py (+0/-31)
ubuntu_sso/utils/qtwisted.py (+1/-3)
ubuntu_sso/utils/tests/test_qtwisted.py (+1/-1)
To merge this branch: bzr merge lp:~diegosarmentero/ubuntu-sso-client/mac-port
Reviewer Review Type Date Requested Status
Eric Casteleijn (community) Approve on 2012-05-01
Manuel de la Peña (community) 2012-04-25 Approve on 2012-05-01
Review via email: mp+103488@code.launchpad.net

Commit Message

- Initial Branch for Mac Port (this comes from urbanape's branch).

To post a comment you must log in.
Manuel de la Peña (mandel) wrote :

I'm getting the following when running the run-mac-tests scripts in my machine:

./run-mac-tests: line 52: python_u1: command not found

Are there any steps I have to follow?

review: Needs Fixing
951. By Diego Sarmentero on 2012-04-25

merge

952. By Diego Sarmentero on 2012-04-27

Updating run mac tests script

953. By Diego Sarmentero on 2012-04-27

merge

954. By Diego Sarmentero on 2012-04-27

ignoring test_ipc

955. By Diego Sarmentero on 2012-04-27

run tests script doesn't setup the PATH, the env-mac script is required if we are using the buildout

Diego Sarmentero (diegosarmentero) wrote :

To run the tests of this branch you will need to download this branch:

bzr branch lp:~diegosarmentero/ubuntuone-windows-installer/mac-env

Execute the Buildout steps as are described in the GDoc.

review: Approve
Eric Casteleijn (thisfred) wrote :

Looks good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/qt/darwin.qss'
2--- data/qt/darwin.qss 1970-01-01 00:00:00 +0000
3+++ data/qt/darwin.qss 2012-04-27 15:54:20 +0000
4@@ -0,0 +1,5 @@
5+/* Styles specific to the windows platform */
6+
7+QWidget {
8+ font-family: "Ubuntu";
9+}
10
11=== modified file 'data/qt/resources.qrc'
12--- data/qt/resources.qrc 2012-03-16 21:24:05 +0000
13+++ data/qt/resources.qrc 2012-04-27 15:54:20 +0000
14@@ -6,6 +6,7 @@
15 <file>../Ubuntu-B.ttf</file>
16 <file>../balloon_shape.png</file>
17 <file>stylesheet.qss</file>
18+ <file>darwin.qss</file>
19 <file>windows.qss</file>
20 <file>linux.qss</file>
21 </qresource>
22
23=== added file 'run-mac-tests'
24--- run-mac-tests 1970-01-01 00:00:00 +0000
25+++ run-mac-tests 2012-04-27 15:54:20 +0000
26@@ -0,0 +1,56 @@
27+#! /bin/bash
28+#
29+# Copyright 2010-2012 Canonical Ltd.
30+#
31+# This program is free software: you can redistribute it and/or modify it
32+# under the terms of the GNU General Public License version 3, as published
33+# by the Free Software Foundation.
34+#
35+# This program is distributed in the hope that it will be useful, but
36+# WITHOUT ANY WARRANTY; without even the implied warranties of
37+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
38+# PURPOSE. See the GNU General Public License for more details.
39+#
40+# You should have received a copy of the GNU General Public License along
41+# with this program. If not, see <http://www.gnu.org/licenses/>.
42+#
43+# In addition, as a special exception, the copyright holders give
44+# permission to link the code of portions of this program with the
45+# OpenSSL library under certain conditions as described in each
46+# individual source file, and distribute linked combinations
47+# including the two.
48+# You must obey the GNU General Public License in all respects
49+# for all of the code used other than OpenSSL. If you modify
50+# file(s) with this exception, you may extend this exception to your
51+# version of the file(s), but you are not obligated to do so. If you
52+# do not wish to do so, delete this exception statement from your
53+# version. If you delete this exception statement from all source
54+# files in the program, then also delete it here.
55+GTK_TESTS_PATH=ubuntu_sso/gtk/tests
56+WINDOWS_TESTS=test_windows.py
57+
58+set -e
59+if [ $# -ne 0 ]; then
60+ # run specific module given by the caller
61+ MODULE="$@"
62+else
63+ # run all tests, useful for tarmac and reviews
64+ MODULE="ubuntu_sso"
65+fi
66+
67+style_check() {
68+ python $u1lint --ignore ubuntu_sso/qt/ui
69+ if [ -x `which pep8` ]; then
70+ pep8 --exclude '.svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py' --repeat . bin/*
71+ else
72+ echo "Please install the 'pep8' package."
73+ fi
74+}
75+
76+echo "*** Running QT test suite for ""$MODULE"" ***"
77+./setup.py build
78+python $u1trial --reactor=qt4 --gui -p "$GTK_TESTS_PATH" -i "test_gui.py, test_linux.py, test_qt.py, test_windows.py, test_glib.py, test_txsecrets.py, test_ipc.py, test_tcpactivation.py" "$MODULE"
79+rm -rf _trial_temp
80+rm -rf build
81+
82+style_check
83
84=== modified file 'run-tests'
85--- run-tests 2012-04-09 17:38:24 +0000
86+++ run-tests 2012-04-27 15:54:20 +0000
87@@ -59,7 +59,7 @@
88 fi
89
90 echo "*** Running GTK test suite for ""$MODULE"" ***"
91-$XVFB_CMDLINE u1trial --reactor=gi --gui -p "$QT_TESTS_PATH" -i "test_windows.py,test_qt.py" "$MODULE"
92+$XVFB_CMDLINE u1trial --reactor=gi --gui -p "$QT_TESTS_PATH" -i "test_windows.py,test_qt.py,test_qtwisted.py,test_pykeyring.py" "$MODULE"
93 rm -rf _trial_temp
94
95 # hack to avoid:
96
97=== modified file 'ubuntu_sso/keyring/__init__.py'
98--- ubuntu_sso/keyring/__init__.py 2012-04-09 17:38:24 +0000
99+++ ubuntu_sso/keyring/__init__.py 2012-04-27 15:54:20 +0000
100@@ -1,8 +1,4 @@
101 # -*- coding: utf-8 -*-
102-# Authors:
103-# Andrew Higginson
104-# Alejandro J. Cura <alecu@canonical.com>
105-# Manuel de la Pena <manuel@canonical.com>
106 #
107 # Copyright 2011-2012 Canonical Ltd.
108 #
109@@ -104,8 +100,8 @@
110 returnValue(None)
111
112
113-if sys.platform == 'win32':
114- from ubuntu_sso.keyring.windows import Keyring
115+if sys.platform in ('win32', 'darwin'):
116+ from ubuntu_sso.keyring.pykeyring import Keyring
117 else:
118 from ubuntu_sso.keyring.linux import Keyring
119
120
121=== modified file 'ubuntu_sso/keyring/linux.py'
122--- ubuntu_sso/keyring/linux.py 2012-04-09 17:38:24 +0000
123+++ ubuntu_sso/keyring/linux.py 2012-04-27 15:54:20 +0000
124@@ -2,12 +2,6 @@
125 #
126 # Copyright (C) 2010-2012 Canonical
127 #
128-# Authors:
129-# Andrew Higginson
130-# Alejandro J. Cura <alecu@canonical.com>
131-# Natalia B. Bidart <natalia.bidart@canonical.com>
132-# Manuel de la Pena <manuel@canonical.com>
133-#
134 # This program is free software; you can redistribute it and/or modify it under
135 # the terms of the GNU General Public License as published by the Free Software
136 # Foundation; version 3.
137
138=== renamed file 'ubuntu_sso/keyring/windows.py' => 'ubuntu_sso/keyring/pykeyring.py'
139--- ubuntu_sso/keyring/windows.py 2012-04-09 17:38:24 +0000
140+++ ubuntu_sso/keyring/pykeyring.py 2012-04-27 15:54:20 +0000
141@@ -1,5 +1,4 @@
142 # -*- coding: utf-8 -*-
143-# Author: Manuel de la Pena <manuel@canonical.com>
144 #
145 # Copyright 2011-2012 Canonical Ltd.
146 #
147@@ -27,11 +26,11 @@
148 # do not wish to do so, delete this exception statement from your
149 # version. If you delete this exception statement from all source
150 # files in the program, then also delete it here.
151-"""Keyring implementation on Windows."""
152+"""Keyring implementation for Windows and Darwin."""
153
154 from json import loads, dumps
155
156-from ubuntu_sso.utils.windows import qtDeferToThread as deferToThread
157+from ubuntu_sso.utils.qtwisted import qtDeferToThread as deferToThread
158
159 USERNAME = 'ubuntu_sso'
160
161
162=== renamed file 'ubuntu_sso/keyring/tests/test_windows.py' => 'ubuntu_sso/keyring/tests/test_pykeyring.py'
163--- ubuntu_sso/keyring/tests/test_windows.py 2012-04-09 17:38:24 +0000
164+++ ubuntu_sso/keyring/tests/test_pykeyring.py 2012-04-27 15:54:20 +0000
165@@ -1,5 +1,4 @@
166 # -*- coding: utf-8 -*-
167-# Author: Manuel de la Pena <manuel@canonical.com>
168 #
169 # Copyright 2011-2012 Canonical Ltd.
170 #
171@@ -27,14 +26,14 @@
172 # do not wish to do so, delete this exception statement from your
173 # version. If you delete this exception statement from all source
174 # files in the program, then also delete it here.
175-"""Test the windows keyring implementation."""
176+"""Test the pykeyring implementation."""
177
178 from json import dumps
179
180 from twisted.internet import defer
181 from twisted.trial.unittest import TestCase
182
183-from ubuntu_sso.keyring import windows
184+from ubuntu_sso.keyring import pykeyring
185
186
187 class FakePyKeyring(object):
188@@ -71,24 +70,24 @@
189 return func(args)
190
191
192-class TestWindowsKeyring(TestCase):
193- """Test the windows keyring implementation."""
194+class TestPyKeyring(TestCase):
195+ """Test the pykeyring implementation."""
196
197 @defer.inlineCallbacks
198 def setUp(self):
199 """Setup tests."""
200- yield super(TestWindowsKeyring, self).setUp()
201+ yield super(TestPyKeyring, self).setUp()
202 self.app_name = 'app_name'
203 self.fake_keyring = FakePyKeyring()
204- self.keyring = windows.Keyring(self.fake_keyring)
205+ self.keyring = pykeyring.Keyring(self.fake_keyring)
206 self.fake_defer = FakeDefer()
207- self.patch(windows, "deferToThread",
208+ self.patch(pykeyring, "deferToThread",
209 self.fake_defer.fake_defer_to_thread)
210
211 # pylint: disable=E1103
212 def test_set_credentials(self):
213 """Test setting the credentials."""
214- self.keyring = windows.Keyring(self.fake_keyring)
215+ self.keyring = pykeyring.Keyring(self.fake_keyring)
216 d = self.keyring.set_credentials(self.app_name, 'password')
217 self.assertEqual(d.data['set_password'],
218 (('app_name', 'ubuntu_sso', '"password"'), ))
219
220=== modified file 'ubuntu_sso/main/__init__.py'
221--- ubuntu_sso/main/__init__.py 2012-04-10 10:43:48 +0000
222+++ ubuntu_sso/main/__init__.py 2012-04-27 15:54:20 +0000
223@@ -57,12 +57,38 @@
224 logger = setup_logging("ubuntu_sso.main")
225 TIMEOUT_INTERVAL = 10000 # 10 seconds
226
227-# pylint: disable=C0103, W0703
228+# pylint: disable=C0103, W0703, W0621
229
230 if sys.platform == 'win32':
231 from ubuntu_sso.main import windows
232+ from ubuntu_sso.main.perspective_broker import (
233+ timeout_func,
234+ shutdown_func,
235+ start_setup,
236+ main as main_func,
237+ )
238+
239 source = windows
240+ source.timeout_func = timeout_func
241+ source.shutdown_func = shutdown_func
242+ source.start_setup = start_setup
243+ source.main = main_func
244+
245 TIMEOUT_INTERVAL = 10000000000 # forever (hack)
246+elif sys.platform == 'darwin':
247+ from ubuntu_sso.main import darwin
248+ from ubuntu_sso.main.perspective_broker import (
249+ timeout_func,
250+ shutdown_func,
251+ start_setup,
252+ main as main_func,
253+ )
254+
255+ source = darwin
256+ source.timeout_func = timeout_func
257+ source.shutdown_func = shutdown_func
258+ source.start_setup = start_setup
259+ source.main = main_func
260 else:
261 from ubuntu_sso.main import linux
262 source = linux
263
264=== added file 'ubuntu_sso/main/darwin.py'
265--- ubuntu_sso/main/darwin.py 1970-01-01 00:00:00 +0000
266+++ ubuntu_sso/main/darwin.py 2012-04-27 15:54:20 +0000
267@@ -0,0 +1,110 @@
268+# -*- coding: utf-8 -*-
269+#
270+# Copyright 2009-2012 Canonical Ltd.
271+#
272+# This program is free software: you can redistribute it and/or modify it
273+# under the terms of the GNU General Public License version 3, as published
274+# by the Free Software Foundation.
275+#
276+# This program is distributed in the hope that it will be useful, but
277+# WITHOUT ANY WARRANTY; without even the implied warranties of
278+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
279+# PURPOSE. See the GNU General Public License for more details.
280+#
281+# You should have received a copy of the GNU General Public License along
282+# with this program. If not, see <http://www.gnu.org/licenses/>.
283+"""Main module implementation specific for darwin (OS X).
284+
285+This module should never import from the multiplatform one (main/__init__.py),
286+but the other way around. Likewise, this module should *not* have any logic
287+regarding error processing or decision making about when to send a given
288+signal.
289+
290+Also, most of the logging is being made in the main module to avoid
291+duplication between the different platform implementations.
292+
293+"""
294+
295+import os
296+import os.path
297+
298+from twisted.internet import defer
299+
300+import ubuntu_sso
301+from ubuntu_sso.logger import setup_logging
302+from ubuntu_sso.utils.ipc import BaseClient
303+from ubuntu_sso.main.perspective_broker import (
304+ SSO_SERVICE_NAME,
305+ SSOLoginClient,
306+ CredentialsManagementClient,
307+ UbuntuSSOProxyBase,
308+ )
309+
310+# Invalid name for signals that are CamelCase
311+# pylint: disable=C0103
312+
313+
314+logger = setup_logging("ubuntu_sso.main.darwin")
315+U1_REG_PATH = r'Software\Ubuntu One'
316+SSO_INSTALL_PATH = 'SSOInstallPath'
317+SSO_BASE_PB_PORT = 50000
318+SSO_RESERVED_PORTS = 3000
319+SSO_PORT_ALLOCATION_STEP = 3 # contiguous ports for sso, u1client, and u1cp
320+
321+
322+def get_sso_pb_port():
323+ """Compute the port the SSO service should run on per-user."""
324+ uid_modulo = os.getuid() % SSO_RESERVED_PORTS
325+ return SSO_BASE_PB_PORT + uid_modulo * SSO_PORT_ALLOCATION_STEP
326+
327+
328+class UbuntuSSOProxy(UbuntuSSOProxyBase):
329+ """Object that exposes the diff referenceable objects."""
330+
331+ name = SSO_SERVICE_NAME
332+
333+ @property
334+ def port(self):
335+ """Get the port on which the SSO pb is running."""
336+ return get_sso_pb_port()
337+
338+ @property
339+ def cmdline(self):
340+ """Get the command line to activate an executable."""
341+ # MAYBE: use special URI schemes to have LaunchServices handle
342+ # running the app, instead of paths to scripts
343+ # e.g. 'x-ubuntuone-syncdaemon', 'x-ubuntu-sso-login'
344+ ubuntu_sso_pkg_dir = os.path.dirname(
345+ os.path.dirname(ubuntu_sso.__file__))
346+ ubuntu_sso_bin_dir = os.path.join(ubuntu_sso_pkg_dir, 'bin')
347+ ubuntu_sso_bin = os.path.join(ubuntu_sso_bin_dir, self.name)
348+ ubuntuone_client_bin_dir = os.path.join(ubuntu_sso_pkg_dir,
349+ '../ubuntuone-client/bin')
350+ ubuntuone_client_bin = os.path.join(ubuntuone_client_bin_dir,
351+ self.name)
352+ if os.path.exists(ubuntuone_client_bin):
353+ return ubuntuone_client_bin
354+ return ubuntu_sso_bin
355+
356+
357+class UbuntuSSOClient(BaseClient):
358+ """Base client that provides remote access to the sso API."""
359+
360+ name = SSO_SERVICE_NAME
361+
362+ clients = {
363+ 'sso_login': SSOLoginClient,
364+ 'cred_manager': CredentialsManagementClient,
365+ }
366+
367+ service_name = UbuntuSSOProxy.name
368+ service_port = UbuntuSSOProxy.port
369+ service_cmdline = UbuntuSSOProxy.cmdline
370+
371+
372+@defer.inlineCallbacks
373+def get_sso_client():
374+ """Get a client to access the SSO service."""
375+ result = UbuntuSSOClient()
376+ yield result.connect()
377+ defer.returnValue(result)
378
379=== added file 'ubuntu_sso/main/perspective_broker.py'
380--- ubuntu_sso/main/perspective_broker.py 1970-01-01 00:00:00 +0000
381+++ ubuntu_sso/main/perspective_broker.py 2012-04-27 15:54:20 +0000
382@@ -0,0 +1,352 @@
383+# -*- coding: utf-8 -*-
384+#
385+# Copyright 2012 Canonical Ltd.
386+#
387+# This program is free software: you can redistribute it and/or modify it
388+# under the terms of the GNU General Public License version 3, as published
389+# by the Free Software Foundation.
390+#
391+# This program is distributed in the hope that it will be useful, but
392+# WITHOUT ANY WARRANTY; without even the implied warranties of
393+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
394+# PURPOSE. See the GNU General Public License for more details.
395+#
396+# You should have received a copy of the GNU General Public License along
397+# with this program. If not, see <http://www.gnu.org/licenses/>.
398+"""Generic SSO client using Twisted Perspective Broker.
399+
400+This module should never import from the multiplatform one (main/__init__.py),
401+but the other way around. Likewise, this module should *not* have any logic
402+regarding error processing or decision making about when to send a given
403+signal.
404+
405+Also, most of the logging is being made in the main module to avoid
406+duplication between the different platform implementations.
407+
408+"""
409+
410+from twisted.internet.task import LoopingCall
411+from twisted.python.failure import Failure
412+
413+from ubuntu_sso.logger import setup_logging
414+from ubuntu_sso.utils.ipc import (
415+ BaseService,
416+ RemoteClient,
417+ RemoteService,
418+ signal,
419+)
420+
421+logger = setup_logging("ubuntu_sso.main.perspective_broker")
422+SSO_SERVICE_NAME = "ubuntu-sso-client"
423+
424+# Invalid name for signals that are CamelCase
425+# pylint: disable=C0103
426+
427+
428+class SSOLoginProxy(RemoteService):
429+ """Login thru the Single Sign On service."""
430+
431+ remote_calls = [
432+ 'generate_captcha',
433+ 'register_user',
434+ 'login',
435+ 'login_and_ping',
436+ 'validate_email',
437+ 'validate_email_and_ping',
438+ 'request_password_reset_token',
439+ 'set_new_password',
440+ ]
441+
442+ def __init__(self, root, *args, **kwargs):
443+ super(SSOLoginProxy, self).__init__(*args, **kwargs)
444+ self.root = root
445+
446+ # generate_capcha signals
447+ @signal
448+ def CaptchaGenerated(self, app_name, result):
449+ """Signal thrown after the captcha is generated."""
450+
451+ @signal
452+ def CaptchaGenerationError(self, app_name, error):
453+ """Signal thrown when there's a problem generating the captcha."""
454+
455+ def generate_captcha(self, app_name, filename):
456+ """Call the matching method in the processor."""
457+ self.root.sso_login.generate_captcha(app_name, filename)
458+
459+ # register_user signals
460+ @signal
461+ def UserRegistered(self, app_name, result):
462+ """Signal thrown when the user is registered."""
463+
464+ @signal
465+ def UserRegistrationError(self, app_name, error):
466+ """Signal thrown when there's a problem registering the user."""
467+
468+ def register_user(self, app_name, email, password, name,
469+ captcha_id, captcha_solution):
470+ """Call the matching method in the processor."""
471+ self.root.sso_login.register_user(app_name, email, password, name,
472+ captcha_id, captcha_solution)
473+
474+ # login signals
475+ @signal
476+ def LoggedIn(self, app_name, result):
477+ """Signal thrown when the user is logged in."""
478+
479+ @signal
480+ def LoginError(self, app_name, error):
481+ """Signal thrown when there is a problem in the login."""
482+
483+ @signal
484+ def UserNotValidated(self, app_name, result):
485+ """Signal thrown when the user is not validated."""
486+
487+ def login(self, app_name, email, password, ping_url=None):
488+ """Call the matching method in the processor."""
489+ self.root.sso_login.login(app_name, email, password, ping_url)
490+
491+ login_and_ping = login
492+
493+ # validate_email signals
494+ @signal
495+ def EmailValidated(self, app_name, result):
496+ """Signal thrown after the email is validated."""
497+
498+ @signal
499+ def EmailValidationError(self, app_name, error):
500+ """Signal thrown when there's a problem validating the email."""
501+
502+ def validate_email(self, app_name, email, password, email_token,
503+ ping_url=None):
504+ """Call the matching method in the processor."""
505+ self.root.sso_login.validate_email(app_name,
506+ email, password, email_token, ping_url)
507+
508+ validate_email_and_ping = validate_email
509+
510+ # request_password_reset_token signals
511+ @signal
512+ def PasswordResetTokenSent(self, app_name, result):
513+ """Signal thrown when the token is succesfully sent."""
514+
515+ @signal
516+ def PasswordResetError(self, app_name, error):
517+ """Signal thrown when there's a problem sending the token."""
518+
519+ def request_password_reset_token(self, app_name, email):
520+ """Call the matching method in the processor."""
521+ self.root.sso_login.request_password_reset_token(app_name, email)
522+
523+ # set_new_password signals
524+ @signal
525+ def PasswordChanged(self, app_name, result):
526+ """Signal thrown when the token is succesfully sent."""
527+
528+ @signal
529+ def PasswordChangeError(self, app_name, error):
530+ """Signal thrown when there's a problem sending the token."""
531+
532+ def set_new_password(self, app_name, email, token, new_password):
533+ """Call the matching method in the processor."""
534+ self.root.sso_login.set_new_password(app_name,
535+ email, token, new_password)
536+
537+
538+class CredentialsManagementProxy(RemoteService):
539+ """Object that manages credentials.
540+
541+ Every exposed method in this class requires one mandatory argument:
542+
543+ - 'app_name': the name of the application. Will be displayed in the
544+ GUI header, plus it will be used to find/build/clear tokens.
545+
546+ And accepts another parameter named 'args', which is a dictionary that
547+ can contain the following:
548+
549+ - 'help_text': an explanatory text for the end-users, will be
550+ shown below the header. This is an optional free text field.
551+
552+ - 'ping_url': the url to open after successful token retrieval. If
553+ defined, the email will be attached to the url and will be pinged
554+ with a OAuth-signed request.
555+
556+ - 'tc_url': the link to the Terms and Conditions page. If defined,
557+ the checkbox to agree to the terms will link to it.
558+
559+ - 'window_id': the id of the window which will be set as a parent
560+ of the GUI. If not defined, no parent will be set.
561+
562+ """
563+
564+ remote_calls = [
565+ 'find_credentials',
566+ 'clear_credentials',
567+ 'store_credentials',
568+ 'register',
569+ 'login',
570+ 'login_email_password',
571+ ]
572+
573+ def __init__(self, root, *args, **kwargs):
574+ super(CredentialsManagementProxy, self).__init__(*args, **kwargs)
575+ self.root = root
576+
577+ @signal
578+ def AuthorizationDenied(self, app_name):
579+ """Signal thrown when the user denies the authorization."""
580+
581+ @signal
582+ def CredentialsFound(self, app_name, credentials):
583+ """Signal thrown when the credentials are found."""
584+
585+ @signal
586+ def CredentialsNotFound(self, app_name):
587+ """Signal thrown when the credentials are not found."""
588+
589+ @signal
590+ def CredentialsCleared(self, app_name):
591+ """Signal thrown when the credentials were cleared."""
592+
593+ @signal
594+ def CredentialsStored(self, app_name):
595+ """Signal thrown when the credentials were cleared."""
596+
597+ @signal
598+ def CredentialsError(self, app_name, error_dict):
599+ """Signal thrown when there is a problem getting the credentials."""
600+
601+ def find_credentials(self, app_name, args):
602+ """Look for the credentials for an application.
603+
604+ - 'app_name': the name of the application which credentials are
605+ going to be removed.
606+
607+ - 'args' is a dictionary, currently not used.
608+
609+ """
610+ self.root.cred_manager.find_credentials(app_name, args)
611+
612+ def clear_credentials(self, app_name, args):
613+ """Clear the credentials for an application.
614+
615+ - 'app_name': the name of the application which credentials are
616+ going to be removed.
617+
618+ - 'args' is a dictionary, currently not used.
619+
620+ """
621+ self.root.cred_manager.clear_credentials(app_name, args)
622+
623+ def store_credentials(self, app_name, args):
624+ """Store the token for an application.
625+
626+ - 'app_name': the name of the application which credentials are
627+ going to be stored.
628+
629+ - 'args' is the dictionary holding the credentials. Needs to provide
630+ the following mandatory keys: 'token', 'token_key', 'consumer_key',
631+ 'consumer_secret'.
632+
633+ """
634+ self.root.cred_manager.store_credentials(app_name, args)
635+
636+ def register(self, app_name, args):
637+ """Get credentials if found else prompt GUI to register."""
638+ self.root.cred_manager.register(app_name, args)
639+
640+ def login(self, app_name, args):
641+ """Get credentials if found else prompt GUI to login."""
642+ self.root.cred_manager.login(app_name, args)
643+
644+ def login_email_password(self, app_name, args):
645+ """Get credentials if found, else login using email and password.
646+
647+ - 'args' should contain at least the follwing keys: 'email' and
648+ 'password'. Those will be used to issue a new SSO token, which will be
649+ returned trough the CredentialsFound signal.
650+
651+ """
652+ self.root.cred_manager.login_email_password(app_name, args)
653+
654+
655+class UbuntuSSOProxyBase(BaseService):
656+ """Object that exposes the diff referenceable objects."""
657+
658+ services = {
659+ 'sso_login': SSOLoginProxy,
660+ 'cred_manager': CredentialsManagementProxy,
661+ }
662+
663+ name = SSO_SERVICE_NAME
664+
665+
666+# ============================== client classes ==============================
667+
668+
669+class SSOLoginClient(RemoteClient):
670+ """Client that can perform calls to the remote SSOLogin object."""
671+
672+ call_remote_functions = SSOLoginProxy.remote_calls
673+ signal_handlers = [
674+ 'CaptchaGenerated',
675+ 'CaptchaGenerationError',
676+ 'UserRegistered',
677+ 'UserRegistrationError',
678+ 'LoggedIn',
679+ 'LoginError',
680+ 'UserNotValidated',
681+ 'EmailValidated',
682+ 'EmailValidationError',
683+ 'PasswordResetTokenSent',
684+ 'PasswordResetError',
685+ 'PasswordChanged',
686+ 'PasswordChangeError',
687+ ]
688+
689+
690+class CredentialsManagementClient(RemoteClient):
691+ """Client that can perform calls to the remote CredManagement object."""
692+
693+ call_remote_functions = CredentialsManagementProxy.remote_calls
694+ signal_handlers = [
695+ 'AuthorizationDenied',
696+ 'CredentialsFound',
697+ 'CredentialsNotFound',
698+ 'CredentialsCleared',
699+ 'CredentialsStored',
700+ 'CredentialsError',
701+ ]
702+
703+
704+def add_timeout(interval, callback, *args, **kwargs):
705+ """Add a timeout callback as a task."""
706+ time_out_task = LoopingCall(callback, *args, **kwargs)
707+ time_out_task.start(interval / 1000, now=False)
708+
709+
710+timeout_func = add_timeout
711+start_setup = lambda *a, **kw: None
712+
713+# the reactor does have run and stop methods
714+# pylint: disable=E1101
715+
716+
717+def shutdown_func():
718+ """Stop the reactor."""
719+ from twisted.internet import reactor
720+ reactor.stop()
721+
722+
723+def finish_setup(result, loop):
724+ """Stop the reactor if a failure ocurred."""
725+ if isinstance(result, Failure):
726+ shutdown_func()
727+
728+
729+def main():
730+ """Run the specific mainloop."""
731+ from twisted.internet import reactor
732+ reactor.run()
733+
734+# pylint: enable=E1101
735
736=== added file 'ubuntu_sso/main/tests/test_darwin.py'
737--- ubuntu_sso/main/tests/test_darwin.py 1970-01-01 00:00:00 +0000
738+++ ubuntu_sso/main/tests/test_darwin.py 2012-04-27 15:54:20 +0000
739@@ -0,0 +1,43 @@
740+# -*- coding: utf-8 -*-
741+#
742+# Copyright 2012 Canonical Ltd.
743+#
744+# This program is free software: you can redistribute it and/or modify it
745+# under the terms of the GNU General Public License version 3, as published
746+# by the Free Software Foundation.
747+#
748+# This program is distributed in the hope that it will be useful, but
749+# WITHOUT ANY WARRANTY; without even the implied warranties of
750+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
751+# PURPOSE. See the GNU General Public License for more details.
752+#
753+# You should have received a copy of the GNU General Public License along
754+# with this program. If not, see <http://www.gnu.org/licenses/>.
755+"""Darwin specific tests for the main module."""
756+
757+from ubuntu_sso.main import darwin
758+from ubuntu_sso.tests import TestCase
759+
760+# because we are using twisted we have java like names C0103
761+# pylint: disable=C0103
762+
763+MOCK_SSO_DARWIN_UID = 1001
764+
765+
766+class MockOsGetuid(object):
767+ """Mocked os.getuid to support reliable uid for test"""
768+
769+ def getuid(self):
770+ """Return the same uid for tests"""
771+ return MOCK_SSO_DARWIN_UID
772+
773+
774+class DarwinTestCase(TestCase):
775+ """Tests for module level misc functions"""
776+
777+ def test_get_sso_pb_port(self):
778+ """Test the get_sso_pb_port function, by patching os.getuid"""
779+ mock_os = MockOsGetuid()
780+ self.patch(darwin, "os", mock_os)
781+ expected_port = 53003
782+ self.assertEqual(darwin.get_sso_pb_port(), expected_port)
783
784=== modified file 'ubuntu_sso/main/tests/test_windows.py'
785--- ubuntu_sso/main/tests/test_windows.py 2012-04-09 17:38:24 +0000
786+++ ubuntu_sso/main/tests/test_windows.py 2012-04-27 15:54:20 +0000
787@@ -32,6 +32,7 @@
788 from _winreg import REG_SZ
789
790 from ubuntu_sso.main import windows
791+from ubuntu_sso.main import perspective_broker
792 from ubuntu_sso.tests import TestCase
793
794 # because we are using twisted we have java like names C0103
795@@ -110,5 +111,6 @@
796 sample_value = "test 123"
797 self.patch(windows, "OpenKey", lambda key, subkey: sample_value)
798 self.patch(windows, "QueryValueEx", lambda key, v: (key, REG_SZ))
799- cmdline = windows.get_activation_cmdline(windows.SSO_SERVICE_NAME)
800+ cmdline = windows.get_activation_cmdline(
801+ perspective_broker.SSO_SERVICE_NAME)
802 self.assertEqual(sample_value, cmdline)
803
804=== modified file 'ubuntu_sso/main/windows.py'
805--- ubuntu_sso/main/windows.py 2012-04-09 17:38:24 +0000
806+++ ubuntu_sso/main/windows.py 2012-04-27 15:54:20 +0000
807@@ -45,350 +45,71 @@
808 # pylint: enable=F0401
809
810 from twisted.internet import defer
811-from twisted.internet.task import LoopingCall
812-from twisted.python.failure import Failure
813
814 from ubuntu_sso.logger import setup_logging
815-from ubuntu_sso.utils.ipc import (
816- BaseClient,
817- BaseService,
818- RemoteClient,
819- RemoteService,
820- signal,
821-)
822-
823-
824-# Invalid name for signals that are CamelCase
825-# pylint: disable=C0103
826+from ubuntu_sso.utils.ipc import BaseClient
827+from ubuntu_sso.main.perspective_broker import (
828+ SSO_SERVICE_NAME,
829+ SSOLoginClient,
830+ CredentialsManagementClient,
831+ UbuntuSSOProxyBase,
832+ )
833
834
835 logger = setup_logging("ubuntu_sso.main.windows")
836-NAMED_PIPE_URL = '\\\\.\\pipe\\ubuntu_sso\\%s'
837 U1_REG_PATH = r'Software\Ubuntu One'
838 SSO_INSTALL_PATH = 'SSOInstallPath'
839 SSO_BASE_PB_PORT = 50000
840 SSO_RESERVED_PORTS = 3000
841 SSO_PORT_ALLOCATION_STEP = 3 # contiguous ports for sso, u1client, and u1cp
842-SSO_SERVICE_NAME = "ubuntu-sso-client"
843
844
845 def get_user_id():
846- """Find the numeric user id."""
847+ """Compute the user id of the currently running process."""
848 process_handle = win32process.GetCurrentProcess()
849 token_handle = win32security.OpenProcessToken(process_handle,
850 win32security.TOKEN_ALL_ACCESS)
851 user_sid = win32security.GetTokenInformation(token_handle,
852 win32security.TokenUser)[0]
853 sid_parts = str(user_sid).split("-")
854- uid = int(sid_parts[-1])
855- return uid
856+ return int(sid_parts[-1])
857
858
859 def get_sso_pb_port():
860- """Get the port on which the SSO pb is running."""
861- uid = get_user_id()
862- uid_modulo = uid % SSO_RESERVED_PORTS
863- port = SSO_BASE_PB_PORT + uid_modulo * SSO_PORT_ALLOCATION_STEP
864- return port
865-
866-
867-def get_activation_cmdline(service_name):
868- """Get the command line to activate an executable."""
869+ """Compute the port the SSO service should run on per-user."""
870+ uid_modulo = get_user_id() % SSO_RESERVED_PORTS
871+ return SSO_BASE_PB_PORT + uid_modulo * SSO_PORT_ALLOCATION_STEP
872+
873+
874+def get_activation_cmdline(name):
875+ """Find the path to the executable callback."""
876 key = OpenKey(HKEY_LOCAL_MACHINE, U1_REG_PATH)
877 # pylint: disable=W0612
878- value, registry_type = QueryValueEx(key, "path-" + service_name)
879+ value, registry_type = QueryValueEx(key, "path-" + name)
880 return value
881
882
883-class SSOLoginProxy(RemoteService):
884- """Login thru the Single Sign On service."""
885-
886- remote_calls = [
887- 'generate_captcha',
888- 'register_user',
889- 'login',
890- 'login_and_ping',
891- 'validate_email',
892- 'validate_email_and_ping',
893- 'request_password_reset_token',
894- 'set_new_password',
895- ]
896-
897- def __init__(self, root, *args, **kwargs):
898- super(SSOLoginProxy, self).__init__(*args, **kwargs)
899- self.root = root
900-
901- # generate_capcha signals
902- @signal
903- def CaptchaGenerated(self, app_name, result):
904- """Signal thrown after the captcha is generated."""
905-
906- @signal
907- def CaptchaGenerationError(self, app_name, error):
908- """Signal thrown when there's a problem generating the captcha."""
909-
910- def generate_captcha(self, app_name, filename):
911- """Call the matching method in the processor."""
912- self.root.sso_login.generate_captcha(app_name, filename)
913-
914- # register_user signals
915- @signal
916- def UserRegistered(self, app_name, result):
917- """Signal thrown when the user is registered."""
918-
919- @signal
920- def UserRegistrationError(self, app_name, error):
921- """Signal thrown when there's a problem registering the user."""
922-
923- def register_user(self, app_name, email, password, name,
924- captcha_id, captcha_solution):
925- """Call the matching method in the processor."""
926- self.root.sso_login.register_user(app_name, email, password, name,
927- captcha_id, captcha_solution)
928-
929- # login signals
930- @signal
931- def LoggedIn(self, app_name, result):
932- """Signal thrown when the user is logged in."""
933-
934- @signal
935- def LoginError(self, app_name, error):
936- """Signal thrown when there is a problem in the login."""
937-
938- @signal
939- def UserNotValidated(self, app_name, result):
940- """Signal thrown when the user is not validated."""
941-
942- def login(self, app_name, email, password, ping_url=None):
943- """Call the matching method in the processor."""
944- self.root.sso_login.login(app_name, email, password, ping_url)
945-
946- login_and_ping = login
947-
948- # validate_email signals
949- @signal
950- def EmailValidated(self, app_name, result):
951- """Signal thrown after the email is validated."""
952-
953- @signal
954- def EmailValidationError(self, app_name, error):
955- """Signal thrown when there's a problem validating the email."""
956-
957- def validate_email(self, app_name, email, password, email_token,
958- ping_url=None):
959- """Call the matching method in the processor."""
960- self.root.sso_login.validate_email(app_name,
961- email, password, email_token, ping_url)
962-
963- validate_email_and_ping = validate_email
964-
965- # request_password_reset_token signals
966- @signal
967- def PasswordResetTokenSent(self, app_name, result):
968- """Signal thrown when the token is succesfully sent."""
969-
970- @signal
971- def PasswordResetError(self, app_name, error):
972- """Signal thrown when there's a problem sending the token."""
973-
974- def request_password_reset_token(self, app_name, email):
975- """Call the matching method in the processor."""
976- self.root.sso_login.request_password_reset_token(app_name, email)
977-
978- # set_new_password signals
979- @signal
980- def PasswordChanged(self, app_name, result):
981- """Signal thrown when the token is succesfully sent."""
982-
983- @signal
984- def PasswordChangeError(self, app_name, error):
985- """Signal thrown when there's a problem sending the token."""
986-
987- def set_new_password(self, app_name, email, token, new_password):
988- """Call the matching method in the processor."""
989- self.root.sso_login.set_new_password(app_name,
990- email, token, new_password)
991-
992-
993-class CredentialsManagementProxy(RemoteService):
994- """Object that manages credentials.
995-
996- Every exposed method in this class requires one mandatory argument:
997-
998- - 'app_name': the name of the application. Will be displayed in the
999- GUI header, plus it will be used to find/build/clear tokens.
1000-
1001- And accepts another parameter named 'args', which is a dictionary that
1002- can contain the following:
1003-
1004- - 'help_text': an explanatory text for the end-users, will be
1005- shown below the header. This is an optional free text field.
1006-
1007- - 'ping_url': the url to open after successful token retrieval. If
1008- defined, the email will be attached to the url and will be pinged
1009- with a OAuth-signed request.
1010-
1011- - 'tc_url': the link to the Terms and Conditions page. If defined,
1012- the checkbox to agree to the terms will link to it.
1013-
1014- - 'window_id': the id of the window which will be set as a parent
1015- of the GUI. If not defined, no parent will be set.
1016-
1017- """
1018-
1019- remote_calls = [
1020- 'find_credentials',
1021- 'clear_credentials',
1022- 'store_credentials',
1023- 'register',
1024- 'login',
1025- 'login_email_password',
1026- ]
1027-
1028- def __init__(self, root, *args, **kwargs):
1029- super(CredentialsManagementProxy, self).__init__(*args, **kwargs)
1030- self.root = root
1031-
1032- @signal
1033- def AuthorizationDenied(self, app_name):
1034- """Signal thrown when the user denies the authorization."""
1035-
1036- @signal
1037- def CredentialsFound(self, app_name, credentials):
1038- """Signal thrown when the credentials are found."""
1039-
1040- @signal
1041- def CredentialsNotFound(self, app_name):
1042- """Signal thrown when the credentials are not found."""
1043-
1044- @signal
1045- def CredentialsCleared(self, app_name):
1046- """Signal thrown when the credentials were cleared."""
1047-
1048- @signal
1049- def CredentialsStored(self, app_name):
1050- """Signal thrown when the credentials were cleared."""
1051-
1052- @signal
1053- def CredentialsError(self, app_name, error_dict):
1054- """Signal thrown when there is a problem getting the credentials."""
1055-
1056- def find_credentials(self, app_name, args):
1057- """Look for the credentials for an application.
1058-
1059- - 'app_name': the name of the application which credentials are
1060- going to be removed.
1061-
1062- - 'args' is a dictionary, currently not used.
1063-
1064- """
1065- self.root.cred_manager.find_credentials(app_name, args)
1066-
1067- def clear_credentials(self, app_name, args):
1068- """Clear the credentials for an application.
1069-
1070- - 'app_name': the name of the application which credentials are
1071- going to be removed.
1072-
1073- - 'args' is a dictionary, currently not used.
1074-
1075- """
1076- self.root.cred_manager.clear_credentials(app_name, args)
1077-
1078- def store_credentials(self, app_name, args):
1079- """Store the token for an application.
1080-
1081- - 'app_name': the name of the application which credentials are
1082- going to be stored.
1083-
1084- - 'args' is the dictionary holding the credentials. Needs to provide
1085- the following mandatory keys: 'token', 'token_key', 'consumer_key',
1086- 'consumer_secret'.
1087-
1088- """
1089- self.root.cred_manager.store_credentials(app_name, args)
1090-
1091- def register(self, app_name, args):
1092- """Get credentials if found else prompt GUI to register."""
1093- self.root.cred_manager.register(app_name, args)
1094-
1095- def login(self, app_name, args):
1096- """Get credentials if found else prompt GUI to login."""
1097- self.root.cred_manager.login(app_name, args)
1098-
1099- def login_email_password(self, app_name, args):
1100- """Get credentials if found, else login using email and password.
1101-
1102- - 'args' should contain at least the follwing keys: 'email' and
1103- 'password'. Those will be used to issue a new SSO token, which will be
1104- returned trough the CredentialsFound signal.
1105-
1106- """
1107- self.root.cred_manager.login_email_password(app_name, args)
1108-
1109-
1110-class UbuntuSSOProxy(BaseService):
1111+class UbuntuSSOProxy(UbuntuSSOProxyBase):
1112 """Object that exposes the diff referenceable objects."""
1113
1114- services = {
1115- 'sso_login': SSOLoginProxy,
1116- 'cred_manager': CredentialsManagementProxy,
1117- }
1118-
1119 name = SSO_SERVICE_NAME
1120
1121 @property
1122 def port(self):
1123- """Get the port of the remote service."""
1124+ """Get the port on which the SSO pb is running."""
1125 return get_sso_pb_port()
1126
1127 @property
1128 def cmdline(self):
1129- """Get the command line to run the service executable."""
1130- return get_activation_cmdline(UbuntuSSOProxy.name)
1131-
1132-
1133-# ============================== client classes ==============================
1134-
1135-
1136-class SSOLoginClient(RemoteClient):
1137- """Client that can perform calls to the remote SSOLogin object."""
1138-
1139- call_remote_functions = SSOLoginProxy.remote_calls
1140- signal_handlers = [
1141- 'CaptchaGenerated',
1142- 'CaptchaGenerationError',
1143- 'UserRegistered',
1144- 'UserRegistrationError',
1145- 'LoggedIn',
1146- 'LoginError',
1147- 'UserNotValidated',
1148- 'EmailValidated',
1149- 'EmailValidationError',
1150- 'PasswordResetTokenSent',
1151- 'PasswordResetError',
1152- 'PasswordChanged',
1153- 'PasswordChangeError',
1154- ]
1155-
1156-
1157-class CredentialsManagementClient(RemoteClient):
1158- """Client that can perform calls to the remote CredManagement object."""
1159-
1160- call_remote_functions = CredentialsManagementProxy.remote_calls
1161- signal_handlers = [
1162- 'AuthorizationDenied',
1163- 'CredentialsFound',
1164- 'CredentialsNotFound',
1165- 'CredentialsCleared',
1166- 'CredentialsStored',
1167- 'CredentialsError',
1168- ]
1169+ """Get the command line to activate an executable."""
1170+ return get_activation_cmdline(self.name)
1171
1172
1173 class UbuntuSSOClient(BaseClient):
1174 """Base client that provides remote access to the sso API."""
1175
1176+ name = SSO_SERVICE_NAME
1177+
1178 clients = {
1179 'sso_login': SSOLoginClient,
1180 'cred_manager': CredentialsManagementClient,
1181@@ -405,36 +126,3 @@
1182 result = UbuntuSSOClient()
1183 yield result.connect()
1184 defer.returnValue(result)
1185-
1186-
1187-def add_timeout(interval, callback, *args, **kwargs):
1188- """Add a timeout callback as a task."""
1189- time_out_task = LoopingCall(callback, *args, **kwargs)
1190- time_out_task.start(interval / 1000, now=False)
1191-
1192-
1193-timeout_func = add_timeout
1194-start_setup = lambda *a, **kw: None
1195-
1196-# the reactor does have run and stop methods
1197-# pylint: disable=E1101
1198-
1199-
1200-def shutdown_func():
1201- """Stop the reactor."""
1202- from twisted.internet import reactor
1203- reactor.stop()
1204-
1205-
1206-def finish_setup(result, loop):
1207- """Stop the reactor if a failure ocurred."""
1208- if isinstance(result, Failure):
1209- shutdown_func()
1210-
1211-
1212-def main():
1213- """Run the specific mainloop."""
1214- from twisted.internet import reactor
1215- reactor.run()
1216-
1217-# pylint: enable=E1101
1218
1219=== modified file 'ubuntu_sso/networkstate/__init__.py'
1220--- ubuntu_sso/networkstate/__init__.py 2012-04-09 17:38:24 +0000
1221+++ ubuntu_sso/networkstate/__init__.py 2012-04-27 15:54:20 +0000
1222@@ -1,5 +1,4 @@
1223 # -*- coding: utf-8 -*-
1224-# Author: Manuel de la Pena <manuel@canonical.com>
1225 #
1226 # Copyright 2011-2012 Canonical Ltd.
1227 #
1228@@ -47,15 +46,16 @@
1229
1230 if sys.platform == 'win32':
1231 from ubuntu_sso.networkstate import windows
1232- NetworkManagerState = windows.NetworkManagerState
1233- ONLINE = windows.ONLINE
1234- OFFLINE = windows.OFFLINE
1235- UNKNOWN = windows.UNKNOWN
1236- is_machine_connected = windows.is_machine_connected
1237+ networksource = windows
1238+elif sys.platform == 'darwin':
1239+ from ubuntu_sso.networkstate import darwin
1240+ networksource = darwin
1241 else:
1242 from ubuntu_sso.networkstate import linux
1243- NetworkManagerState = linux.NetworkManagerState
1244- ONLINE = linux.ONLINE
1245- OFFLINE = linux.OFFLINE
1246- UNKNOWN = linux.UNKNOWN
1247- is_machine_connected = linux.is_machine_connected
1248+ networksource = linux
1249+
1250+NetworkManagerState = networksource.NetworkManagerState
1251+ONLINE = networksource.ONLINE
1252+OFFLINE = networksource.OFFLINE
1253+UNKNOWN = networksource.UNKNOWN
1254+is_machine_connected = networksource.is_machine_connected
1255
1256=== added file 'ubuntu_sso/networkstate/darwin.py'
1257--- ubuntu_sso/networkstate/darwin.py 1970-01-01 00:00:00 +0000
1258+++ ubuntu_sso/networkstate/darwin.py 2012-04-27 15:54:20 +0000
1259@@ -0,0 +1,95 @@
1260+# -*- coding: utf-8 -*-
1261+#
1262+# Copyright 2012 Canonical Ltd.
1263+#
1264+# This program is free software: you can redistribute it and/or modify it
1265+# under the terms of the GNU General Public License version 3, as published
1266+# by the Free Software Foundation.
1267+#
1268+# This program is distributed in the hope that it will be useful, but
1269+# WITHOUT ANY WARRANTY; without even the implied warranties of
1270+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1271+# PURPOSE. See the GNU General Public License for more details.
1272+#
1273+# You should have received a copy of the GNU General Public License along
1274+# with this program. If not, see <http://www.gnu.org/licenses/>.
1275+"""Implementation of network state detection."""
1276+
1277+from twisted.internet import defer
1278+
1279+from ubuntu_sso.networkstate import NetworkFailException
1280+from ubuntu_sso.networkstate.networkstates import (
1281+ ONLINE, OFFLINE, UNKNOWN,
1282+
1283+ NM_STATE_CONNECTING_LIST,
1284+ NM_STATE_CONNECTED_LIST,
1285+ NM_STATE_DISCONNECTED_LIST,
1286+ )
1287+from ubuntu_sso.networkstate.networkstates import NM_STATE_CONNECTED_GLOBAL
1288+from ubuntu_sso.logger import setup_logging
1289+logger = setup_logging("ubuntu_sso.networkstate")
1290+
1291+
1292+class NetworkManagerState(object):
1293+ """All likely to change very soon."""
1294+
1295+ def __init__(self, result_cb):
1296+ """Initialize this instance with a result and error callbacks."""
1297+ self.result_cb = result_cb
1298+ self.state_signal = None
1299+
1300+ def call_result_cb(self, state):
1301+ """Return the state thru the result callback."""
1302+ self.result_cb(state)
1303+
1304+ def got_state(self, state):
1305+ """Not actually called by DBus when the state is retrieved from NM."""
1306+ if state in NM_STATE_CONNECTED_LIST:
1307+ self.call_result_cb(ONLINE)
1308+ elif state in NM_STATE_CONNECTING_LIST:
1309+ logger.debug("Currently connecting, waiting for signal")
1310+ else:
1311+ self.call_result_cb(OFFLINE)
1312+
1313+ def got_error(self, error):
1314+ """Not actually called by DBus when the state is retrieved from NM."""
1315+ logger.error("Error contacting NetworkManager: %s" % \
1316+ str(error))
1317+ self.call_result_cb(UNKNOWN)
1318+
1319+ def state_changed(self, state):
1320+ """Called when a signal is emmited by Network Manager."""
1321+ if int(state) in NM_STATE_CONNECTED_LIST:
1322+ self.call_result_cb(ONLINE)
1323+ elif int(state) in NM_STATE_DISCONNECTED_LIST:
1324+ self.call_result_cb(OFFLINE)
1325+ else:
1326+ logger.debug("Not yet connected: continuing to wait")
1327+
1328+ def find_online_state(self):
1329+ """Get the network state and return it thru the set callback."""
1330+ try:
1331+ self.state_changed(NM_STATE_CONNECTED_GLOBAL)
1332+ except Exception, e: # pylint: disable=W0703
1333+ self.got_error(e)
1334+
1335+
1336+def is_machine_connected():
1337+ """Return a deferred that when fired, returns if the machine is online."""
1338+ d = defer.Deferred()
1339+
1340+ def got_state(state):
1341+ """The state was retrieved from the Network Manager."""
1342+ result = int(state) in NM_STATE_CONNECTED_LIST
1343+ d.callback(result)
1344+
1345+ # pylint: disable=W0702, W0703
1346+ try:
1347+ network = NetworkManagerState(got_state)
1348+ network.find_online_state()
1349+ except Exception, e:
1350+ logger.exception('is_machine_connected failed with:')
1351+ d.errback(NetworkFailException(e))
1352+ # pylint: enable=W0702, W0703
1353+
1354+ return d
1355
1356=== modified file 'ubuntu_sso/networkstate/linux.py'
1357--- ubuntu_sso/networkstate/linux.py 2012-04-09 17:38:24 +0000
1358+++ ubuntu_sso/networkstate/linux.py 2012-04-27 15:54:20 +0000
1359@@ -1,9 +1,5 @@
1360 # -*- coding: utf-8 -*-
1361 #
1362-# networkstate - detect the current state of the network
1363-#
1364-# Author: Alejandro J. Cura <alecu@canonical.com>
1365-#
1366 # Copyright 2010-2012 Canonical Ltd.
1367 #
1368 # This program is free software: you can redistribute it and/or modify it
1369@@ -37,44 +33,16 @@
1370 from twisted.internet import defer
1371
1372 from ubuntu_sso.networkstate import NetworkFailException
1373+from ubuntu_sso.networkstate.networkstates import (
1374+ ONLINE, OFFLINE, UNKNOWN,
1375+
1376+ NM_STATE_CONNECTING_LIST,
1377+ NM_STATE_CONNECTED_LIST,
1378+ NM_STATE_DISCONNECTED_LIST,
1379+ )
1380 from ubuntu_sso.logger import setup_logging
1381 logger = setup_logging("ubuntu_sso.networkstate")
1382
1383-# Values returned by the callback
1384-ONLINE, OFFLINE, UNKNOWN = object(), object(), object()
1385-
1386-NM_STATE_NAMES = {
1387- ONLINE: "online",
1388- OFFLINE: "offline",
1389- UNKNOWN: "unknown",
1390-}
1391-
1392-# Internal NetworkManager State constants
1393-NM_STATE_UNKNOWN = 0
1394-NM_STATE_UNKNOWN_LIST = [NM_STATE_UNKNOWN]
1395-NM_STATE_ASLEEP_OLD = 1
1396-NM_STATE_ASLEEP = 10
1397-NM_STATE_ASLEEP_LIST = [NM_STATE_ASLEEP_OLD,
1398- NM_STATE_ASLEEP]
1399-NM_STATE_CONNECTING_OLD = 2
1400-NM_STATE_CONNECTING = 40
1401-NM_STATE_CONNECTING_LIST = [NM_STATE_CONNECTING_OLD,
1402- NM_STATE_CONNECTING]
1403-NM_STATE_CONNECTED_OLD = 3
1404-NM_STATE_CONNECTED_LOCAL = 50
1405-NM_STATE_CONNECTED_SITE = 60
1406-NM_STATE_CONNECTED_GLOBAL = 70
1407-# Specifically don't include local and site, as they won't let us get to server
1408-NM_STATE_CONNECTED_LIST = [NM_STATE_CONNECTED_OLD,
1409- NM_STATE_CONNECTED_GLOBAL]
1410-NM_STATE_DISCONNECTED_OLD = 4
1411-NM_STATE_DISCONNECTED = 20
1412-# For us, local and site connections are the same as diconnected
1413-NM_STATE_DISCONNECTED_LIST = [NM_STATE_DISCONNECTED_OLD,
1414- NM_STATE_DISCONNECTED,
1415- NM_STATE_CONNECTED_LOCAL,
1416- NM_STATE_CONNECTED_SITE]
1417-
1418 NM_DBUS_INTERFACE = "org.freedesktop.NetworkManager"
1419 NM_DBUS_OBJECTPATH = "/org/freedesktop/NetworkManager"
1420 DBUS_UNKNOWN_SERVICE = "org.freedesktop.DBus.Error.ServiceUnknown"
1421
1422=== added file 'ubuntu_sso/networkstate/networkstates.py'
1423--- ubuntu_sso/networkstate/networkstates.py 1970-01-01 00:00:00 +0000
1424+++ ubuntu_sso/networkstate/networkstates.py 2012-04-27 15:54:20 +0000
1425@@ -0,0 +1,51 @@
1426+# -*- coding: utf-8 -*-
1427+#
1428+# Copyright 2012 Canonical Ltd.
1429+#
1430+# This program is free software: you can redistribute it and/or modify it
1431+# under the terms of the GNU General Public License version 3, as published
1432+# by the Free Software Foundation.
1433+#
1434+# This program is distributed in the hope that it will be useful, but
1435+# WITHOUT ANY WARRANTY; without even the implied warranties of
1436+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1437+# PURPOSE. See the GNU General Public License for more details.
1438+#
1439+# You should have received a copy of the GNU General Public License along
1440+# with this program. If not, see <http://www.gnu.org/licenses/>.
1441+"""Network states."""
1442+
1443+# Values returned by the callback
1444+ONLINE, OFFLINE, UNKNOWN = object(), object(), object()
1445+
1446+NM_STATE_NAMES = {
1447+ ONLINE: "online",
1448+ OFFLINE: "offline",
1449+ UNKNOWN: "unknown",
1450+}
1451+
1452+# Internal NetworkManager State constants
1453+NM_STATE_UNKNOWN = 0
1454+NM_STATE_UNKNOWN_LIST = [NM_STATE_UNKNOWN]
1455+NM_STATE_ASLEEP_OLD = 1
1456+NM_STATE_ASLEEP = 10
1457+NM_STATE_ASLEEP_LIST = [NM_STATE_ASLEEP_OLD,
1458+ NM_STATE_ASLEEP]
1459+NM_STATE_CONNECTING_OLD = 2
1460+NM_STATE_CONNECTING = 40
1461+NM_STATE_CONNECTING_LIST = [NM_STATE_CONNECTING_OLD,
1462+ NM_STATE_CONNECTING]
1463+NM_STATE_CONNECTED_OLD = 3
1464+NM_STATE_CONNECTED_LOCAL = 50
1465+NM_STATE_CONNECTED_SITE = 60
1466+NM_STATE_CONNECTED_GLOBAL = 70
1467+# Specifically don't include local and site, as they won't let us get to server
1468+NM_STATE_CONNECTED_LIST = [NM_STATE_CONNECTED_OLD,
1469+ NM_STATE_CONNECTED_GLOBAL]
1470+NM_STATE_DISCONNECTED_OLD = 4
1471+NM_STATE_DISCONNECTED = 20
1472+# For us, local and site connections are the same as diconnected
1473+NM_STATE_DISCONNECTED_LIST = [NM_STATE_DISCONNECTED_OLD,
1474+ NM_STATE_DISCONNECTED,
1475+ NM_STATE_CONNECTED_LOCAL,
1476+ NM_STATE_CONNECTED_SITE]
1477
1478=== modified file 'ubuntu_sso/networkstate/tests/test_linux.py'
1479--- ubuntu_sso/networkstate/tests/test_linux.py 2012-04-09 17:38:24 +0000
1480+++ ubuntu_sso/networkstate/tests/test_linux.py 2012-04-27 15:54:20 +0000
1481@@ -1,9 +1,5 @@
1482 # -*- coding: utf-8 -*-
1483 #
1484-# test_networkstate - tests for ubuntu_sso.networkstate
1485-#
1486-# Author: Alejandro J. Cura <alecu@canonical.com>
1487-#
1488 # Copyright 2010-2012 Canonical Ltd.
1489 #
1490 # This program is free software: you can redistribute it and/or modify it
1491@@ -42,13 +38,10 @@
1492 linux,
1493 NetworkFailException,
1494 NetworkManagerState,
1495+)
1496+
1497+from ubuntu_sso.networkstate.networkstates import (
1498 ONLINE, OFFLINE, UNKNOWN,
1499-)
1500-
1501-from ubuntu_sso.networkstate.linux import (is_machine_connected,
1502- DBUS_UNKNOWN_SERVICE,
1503- NM_DBUS_INTERFACE,
1504- NM_DBUS_OBJECTPATH,
1505 NM_STATE_ASLEEP,
1506 NM_STATE_ASLEEP_OLD,
1507 NM_STATE_CONNECTING,
1508@@ -62,6 +55,12 @@
1509 NM_STATE_UNKNOWN,
1510 )
1511
1512+from ubuntu_sso.networkstate.linux import (is_machine_connected,
1513+ DBUS_UNKNOWN_SERVICE,
1514+ NM_DBUS_INTERFACE,
1515+ NM_DBUS_OBJECTPATH,
1516+)
1517+
1518
1519 class TestException(Exception):
1520 """An exception to test error conditions."""
1521
1522=== modified file 'ubuntu_sso/qt/__init__.py'
1523--- ubuntu_sso/qt/__init__.py 2012-04-09 17:38:24 +0000
1524+++ ubuntu_sso/qt/__init__.py 2012-04-27 15:54:20 +0000
1525@@ -28,6 +28,7 @@
1526 # files in the program, then also delete it here.
1527 """The Qt graphical interface for the Ubuntu Single Sign On Client."""
1528
1529+import sys
1530 import collections
1531
1532 from PyQt4 import QtGui, QtCore
1533@@ -46,6 +47,12 @@
1534 TITLE_STYLE = u'<span style="font-size:xx-large;font-weight:bold;">%s</span>'
1535 WINDOW_TITLE = 'Ubuntu Single Sign On'
1536
1537+# TODO: There is a pixel discrepancy between Qt on Linux and Windows
1538+# and Mac OS X. On Mac OS X, one test fails with the height being
1539+# off. For now, we're forcing a different UI height on darwin.
1540+if sys.platform == 'darwin':
1541+ PREFERED_UI_SIZE['height'] = 533
1542+
1543
1544 # Based on the gtk implementation
1545 def build_general_error_message(errordict):
1546
1547=== modified file 'ubuntu_sso/qt/main/__init__.py'
1548--- ubuntu_sso/qt/main/__init__.py 2012-04-09 17:38:24 +0000
1549+++ ubuntu_sso/qt/main/__init__.py 2012-04-27 15:54:20 +0000
1550@@ -41,7 +41,7 @@
1551
1552
1553 # Invalid name "source", pylint: disable=C0103
1554-if sys.platform == 'win32':
1555+if sys.platform in ('win32', 'darwin'):
1556 from ubuntu_sso.qt.main import windows
1557 source = windows
1558 else:
1559
1560=== modified file 'ubuntu_sso/utils/__init__.py'
1561--- ubuntu_sso/utils/__init__.py 2012-04-09 17:38:24 +0000
1562+++ ubuntu_sso/utils/__init__.py 2012-04-27 15:54:20 +0000
1563@@ -47,11 +47,13 @@
1564 BIN_SUFFIX = 'bin'
1565 DATA_SUFFIX = 'data'
1566
1567-if sys.platform == "win32":
1568- from ubuntu_sso.utils import windows as source
1569-else:
1570- from ubuntu_sso.utils import linux as source
1571-PLATFORM_QSS = source.PLATFORM_QSS
1572+QSS_MAP = dict(win32=':/windows.qss',
1573+ darwin=':/darwin.qss',
1574+ linux=':/linux.qss')
1575+
1576+# Setting linux as default if we don't find the
1577+# platform as a key in the dictionary
1578+PLATFORM_QSS = QSS_MAP.get(sys.platform, ":/linux.qss")
1579
1580
1581 def _get_dir(dir_name, dir_constant):
1582@@ -72,7 +74,8 @@
1583
1584 # otherwise, try to load 'dir_constant' from installation path
1585 try:
1586- # Unused variable 'ubuntu_sso', pylint: disable=W0612, F0401, E0611
1587+ # Unused variable 'ubuntu_sso'
1588+ # pylint: disable=W0612, F0401, E0611
1589 import ubuntu_sso.constants
1590 module = sys.modules.get('ubuntu_sso.constants')
1591 return getattr(module, dir_constant)
1592
1593=== removed file 'ubuntu_sso/utils/linux.py'
1594--- ubuntu_sso/utils/linux.py 2012-04-09 17:38:24 +0000
1595+++ ubuntu_sso/utils/linux.py 1970-01-01 00:00:00 +0000
1596@@ -1,31 +0,0 @@
1597-# -*- coding: utf-8 -*-
1598-#
1599-# Copyright 2010-2012 Canonical Ltd.
1600-#
1601-# This program is free software: you can redistribute it and/or modify it
1602-# under the terms of the GNU General Public License version 3, as published
1603-# by the Free Software Foundation.
1604-#
1605-# This program is distributed in the hope that it will be useful, but
1606-# WITHOUT ANY WARRANTY; without even the implied warranties of
1607-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1608-# PURPOSE. See the GNU General Public License for more details.
1609-#
1610-# You should have received a copy of the GNU General Public License along
1611-# with this program. If not, see <http://www.gnu.org/licenses/>.
1612-#
1613-# In addition, as a special exception, the copyright holders give
1614-# permission to link the code of portions of this program with the
1615-# OpenSSL library under certain conditions as described in each
1616-# individual source file, and distribute linked combinations
1617-# including the two.
1618-# You must obey the GNU General Public License in all respects
1619-# for all of the code used other than OpenSSL. If you modify
1620-# file(s) with this exception, you may extend this exception to your
1621-# version of the file(s), but you are not obligated to do so. If you
1622-# do not wish to do so, delete this exception statement from your
1623-# version. If you delete this exception statement from all source
1624-# files in the program, then also delete it here.
1625-"""Platform specific constants and functions (for Linux)."""
1626-
1627-PLATFORM_QSS = ":/linux.qss"
1628
1629=== renamed file 'ubuntu_sso/utils/windows.py' => 'ubuntu_sso/utils/qtwisted.py'
1630--- ubuntu_sso/utils/windows.py 2012-04-09 17:38:24 +0000
1631+++ ubuntu_sso/utils/qtwisted.py 2012-04-27 15:54:20 +0000
1632@@ -26,9 +26,7 @@
1633 # do not wish to do so, delete this exception statement from your
1634 # version. If you delete this exception statement from all source
1635 # files in the program, then also delete it here.
1636-"""Platform specific constants and functions (for Windows)."""
1637-
1638-PLATFORM_QSS = ":/windows.qss"
1639+"""Platform specific constants and functions (for Qt/Twisted)."""
1640
1641 from twisted.internet import defer
1642 from PyQt4.QtCore import QThread, QCoreApplication
1643
1644=== renamed file 'ubuntu_sso/utils/tests/test_windows.py' => 'ubuntu_sso/utils/tests/test_qtwisted.py'
1645--- ubuntu_sso/utils/tests/test_windows.py 2012-04-09 17:38:24 +0000
1646+++ ubuntu_sso/utils/tests/test_qtwisted.py 2012-04-27 15:54:20 +0000
1647@@ -33,7 +33,7 @@
1648 from twisted.internet import defer
1649 from twisted.trial.unittest import TestCase
1650
1651-from ubuntu_sso.utils.windows import qtDeferToThread
1652+from ubuntu_sso.utils.qtwisted import qtDeferToThread
1653
1654
1655 class FakeException(Exception):

Subscribers

People subscribed via source and target branches