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