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