Merge lp:~alecu/ubuntu-sso-client/timestamp-autofix-1-0 into lp:ubuntu-sso-client/stable-1-0
- timestamp-autofix-1-0
- Merge into stable-1-0
Proposed by
Alejandro J. Cura
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Natalia Bidart | ||||||||
Approved revision: | 649 | ||||||||
Merged at revision: | 647 | ||||||||
Proposed branch: | lp:~alecu/ubuntu-sso-client/timestamp-autofix-1-0 | ||||||||
Merge into: | lp:ubuntu-sso-client/stable-1-0 | ||||||||
Diff against target: |
431 lines (+315/-6) 5 files modified
ubuntu_sso/main.py (+32/-4) ubuntu_sso/tests/test_main.py (+38/-2) ubuntu_sso/utils/__init__.py (+81/-0) ubuntu_sso/utils/tests/__init__.py (+16/-0) ubuntu_sso/utils/tests/test_oauth_headers.py (+148/-0) |
||||||||
To merge this branch: | bzr merge lp:~alecu/ubuntu-sso-client/timestamp-autofix-1-0 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Natalia Bidart (community) | Approve | ||
Diego Sarmentero (community) | Approve | ||
Review via email: mp+82737@code.launchpad.net |
To post a comment you must log in.
Revision history for this message
Diego Sarmentero (diegosarmentero) : | # |
review:
Abstain
- 649. By Alejandro J. Cura
-
fix year in file headers
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote : | # |
+1 looks great!
review:
Approve
Revision history for this message
Natalia Bidart (nataliabidart) wrote : | # |
Tested locally and IRL. It works great, and all tests are green.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'ubuntu_sso/main.py' | |||
2 | --- ubuntu_sso/main.py 2011-03-29 17:26:26 +0000 | |||
3 | +++ ubuntu_sso/main.py 2011-11-23 19:50:27 +0000 | |||
4 | @@ -3,7 +3,7 @@ | |||
5 | 3 | # Author: Natalia Bidart <natalia.bidart@canonical.com> | 3 | # Author: Natalia Bidart <natalia.bidart@canonical.com> |
6 | 4 | # Author: Alejandro J. Cura <alecu@canonical.com> | 4 | # Author: Alejandro J. Cura <alecu@canonical.com> |
7 | 5 | # | 5 | # |
9 | 6 | # Copyright 2009 Canonical Ltd. | 6 | # Copyright 2009, 2011 Canonical Ltd. |
10 | 7 | # | 7 | # |
11 | 8 | # This program is free software: you can redistribute it and/or modify it | 8 | # This program is free software: you can redistribute it and/or modify it |
12 | 9 | # under the terms of the GNU General Public License version 3, as published | 9 | # under the terms of the GNU General Public License version 3, as published |
13 | @@ -46,6 +46,7 @@ | |||
14 | 46 | from ubuntu_sso import DBUS_IFACE_USER_NAME, DBUS_IFACE_CRED_NAME | 46 | from ubuntu_sso import DBUS_IFACE_USER_NAME, DBUS_IFACE_CRED_NAME |
15 | 47 | from ubuntu_sso.keyring import Keyring, get_token_name, U1_APP_NAME | 47 | from ubuntu_sso.keyring import Keyring, get_token_name, U1_APP_NAME |
16 | 48 | from ubuntu_sso.logger import setup_logging | 48 | from ubuntu_sso.logger import setup_logging |
17 | 49 | from ubuntu_sso.utils import timestamp_checker | ||
18 | 49 | 50 | ||
19 | 50 | 51 | ||
20 | 51 | # Disable the invalid name warning, as we have a lot of DBus style names | 52 | # Disable the invalid name warning, as we have a lot of DBus style names |
21 | @@ -117,6 +118,27 @@ | |||
22 | 117 | return creds | 118 | return creds |
23 | 118 | 119 | ||
24 | 119 | 120 | ||
25 | 121 | class TimestampedAuthorizer(OAuthAuthorizer): | ||
26 | 122 | """Includes a custom timestamp on OAuth signatures.""" | ||
27 | 123 | |||
28 | 124 | def __init__(self, get_timestamp, *args, **kwargs): | ||
29 | 125 | """Store the get_timestamp method, and move on.""" | ||
30 | 126 | OAuthAuthorizer.__init__(self, *args, **kwargs) | ||
31 | 127 | self.get_timestamp = get_timestamp | ||
32 | 128 | |||
33 | 129 | # pylint: disable=C0103,E1101 | ||
34 | 130 | def authorizeRequest(self, absolute_uri, method, body, headers): | ||
35 | 131 | """Override authorizeRequest including the timestamp.""" | ||
36 | 132 | parameters = {"oauth_timestamp": self.get_timestamp()} | ||
37 | 133 | oauth_request = oauth.OAuthRequest.from_consumer_and_token( | ||
38 | 134 | self.consumer, self.access_token, http_url=absolute_uri, | ||
39 | 135 | parameters=parameters) | ||
40 | 136 | oauth_request.sign_request( | ||
41 | 137 | oauth.OAuthSignatureMethod_PLAINTEXT(), | ||
42 | 138 | self.consumer, self.access_token) | ||
43 | 139 | headers.update(oauth_request.to_header(self.oauth_realm)) | ||
44 | 140 | |||
45 | 141 | |||
46 | 120 | class SSOLoginProcessor(object): | 142 | class SSOLoginProcessor(object): |
47 | 121 | """Login and register users using the Ubuntu Single Sign On service.""" | 143 | """Login and register users using the Ubuntu Single Sign On service.""" |
48 | 122 | 144 | ||
49 | @@ -236,7 +258,9 @@ | |||
50 | 236 | if sso_service is None: | 258 | if sso_service is None: |
51 | 237 | oauth_token = oauth.OAuthToken(token['token'], | 259 | oauth_token = oauth.OAuthToken(token['token'], |
52 | 238 | token['token_secret']) | 260 | token['token_secret']) |
54 | 239 | authorizer = OAuthAuthorizer(token['consumer_key'], | 261 | authorizer = TimestampedAuthorizer( |
55 | 262 | timestamp_checker.get_faithful_time, | ||
56 | 263 | token['consumer_key'], | ||
57 | 240 | token['consumer_secret'], | 264 | token['consumer_secret'], |
58 | 241 | oauth_token) | 265 | oauth_token) |
59 | 242 | sso_service = self.sso_service_class(authorizer, self.service_url) | 266 | sso_service = self.sso_service_class(authorizer, self.service_url) |
60 | @@ -258,7 +282,8 @@ | |||
61 | 258 | token_name=token_name) | 282 | token_name=token_name) |
62 | 259 | 283 | ||
63 | 260 | oauth_token = oauth.OAuthToken(token['token'], token['token_secret']) | 284 | oauth_token = oauth.OAuthToken(token['token'], token['token_secret']) |
65 | 261 | authorizer = OAuthAuthorizer(token['consumer_key'], | 285 | authorizer = TimestampedAuthorizer( |
66 | 286 | timestamp_checker.get_faithful_time, | ||
67 | 262 | token['consumer_secret'], | 287 | token['consumer_secret'], |
68 | 263 | oauth_token) | 288 | oauth_token) |
69 | 264 | sso_service = self.sso_service_class(authorizer, self.service_url) | 289 | sso_service = self.sso_service_class(authorizer, self.service_url) |
70 | @@ -607,9 +632,12 @@ | |||
71 | 607 | credentials['consumer_secret']) | 632 | credentials['consumer_secret']) |
72 | 608 | token = oauth.OAuthToken(credentials['token'], | 633 | token = oauth.OAuthToken(credentials['token'], |
73 | 609 | credentials['token_secret']) | 634 | credentials['token_secret']) |
74 | 635 | timestamp = timestamp_checker.get_faithful_time() | ||
75 | 636 | parameters = {"oauth_timestamp": timestamp} | ||
76 | 610 | get_request = oauth.OAuthRequest.from_consumer_and_token | 637 | get_request = oauth.OAuthRequest.from_consumer_and_token |
77 | 611 | oauth_req = get_request(oauth_consumer=consumer, token=token, | 638 | oauth_req = get_request(oauth_consumer=consumer, token=token, |
79 | 612 | http_method="GET", http_url=url) | 639 | http_method="GET", http_url=url, |
80 | 640 | parameters=parameters) | ||
81 | 613 | oauth_req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), | 641 | oauth_req.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), |
82 | 614 | consumer, token) | 642 | consumer, token) |
83 | 615 | request = urllib2.Request(url, headers=oauth_req.to_header()) | 643 | request = urllib2.Request(url, headers=oauth_req.to_header()) |
84 | 616 | 644 | ||
85 | === modified file 'ubuntu_sso/tests/test_main.py' | |||
86 | --- ubuntu_sso/tests/test_main.py 2011-03-29 17:26:26 +0000 | |||
87 | +++ ubuntu_sso/tests/test_main.py 2011-11-23 19:50:27 +0000 | |||
88 | @@ -5,7 +5,7 @@ | |||
89 | 5 | # Author: Natalia Bidart <natalia.bidart@canonical.com> | 5 | # Author: Natalia Bidart <natalia.bidart@canonical.com> |
90 | 6 | # Author: Alejandro J. Cura <alecu@canonical.com> | 6 | # Author: Alejandro J. Cura <alecu@canonical.com> |
91 | 7 | # | 7 | # |
93 | 8 | # Copyright 2009-2010 Canonical Ltd. | 8 | # Copyright 2009-2011 Canonical Ltd. |
94 | 9 | # | 9 | # |
95 | 10 | # This program is free software: you can redistribute it and/or modify it | 10 | # This program is free software: you can redistribute it and/or modify it |
96 | 11 | # under the terms of the GNU General Public License version 3, as published | 11 | # under the terms of the GNU General Public License version 3, as published |
97 | @@ -22,6 +22,7 @@ | |||
98 | 22 | 22 | ||
99 | 23 | import logging | 23 | import logging |
100 | 24 | import os | 24 | import os |
101 | 25 | import time | ||
102 | 25 | import urllib2 | 26 | import urllib2 |
103 | 26 | 27 | ||
104 | 27 | import gobject | 28 | import gobject |
105 | @@ -31,6 +32,7 @@ | |||
106 | 31 | from lazr.restfulclient.errors import HTTPError | 32 | from lazr.restfulclient.errors import HTTPError |
107 | 32 | # pylint: enable=F0401 | 33 | # pylint: enable=F0401 |
108 | 33 | from mocker import Mocker, MockerTestCase, ARGS, KWARGS, ANY | 34 | from mocker import Mocker, MockerTestCase, ARGS, KWARGS, ANY |
109 | 35 | from oauth import oauth | ||
110 | 34 | from twisted.internet.defer import Deferred | 36 | from twisted.internet.defer import Deferred |
111 | 35 | from twisted.trial.unittest import TestCase | 37 | from twisted.trial.unittest import TestCase |
112 | 36 | 38 | ||
113 | @@ -46,7 +48,7 @@ | |||
114 | 46 | keyring_get_credentials, keyring_store_credentials, logger, | 48 | keyring_get_credentials, keyring_store_credentials, logger, |
115 | 47 | NewPasswordError, PING_URL, SERVICE_URL, | 49 | NewPasswordError, PING_URL, SERVICE_URL, |
116 | 48 | RegistrationError, ResetPasswordTokenError, | 50 | RegistrationError, ResetPasswordTokenError, |
118 | 49 | SSOCredentials, SSOLogin, SSOLoginProcessor) | 51 | SSOCredentials, SSOLogin, SSOLoginProcessor, TimestampedAuthorizer) |
119 | 50 | 52 | ||
120 | 51 | 53 | ||
121 | 52 | # Access to a protected member 'yyy' of a client class | 54 | # Access to a protected member 'yyy' of a client class |
122 | @@ -193,6 +195,38 @@ | |||
123 | 193 | self.accounts = FakedAccounts() | 195 | self.accounts = FakedAccounts() |
124 | 194 | 196 | ||
125 | 195 | 197 | ||
126 | 198 | class TimestampedAuthorizerTestCase(TestCase): | ||
127 | 199 | """Test suite for the TimestampedAuthorizer.""" | ||
128 | 200 | |||
129 | 201 | def test_authorize_request_includes_timestamp(self): | ||
130 | 202 | """The authorizeRequest method includes the timestamp.""" | ||
131 | 203 | fromcandt_call = [] | ||
132 | 204 | fake_uri = "http://protocultura.net" | ||
133 | 205 | fake_timestamp = 1234 | ||
134 | 206 | get_fake_timestamp = lambda: fake_timestamp | ||
135 | 207 | original_oauthrequest = oauth.OAuthRequest | ||
136 | 208 | |||
137 | 209 | class FakeOAuthRequest(oauth.OAuthRequest): | ||
138 | 210 | """A Fake OAuthRequest class.""" | ||
139 | 211 | |||
140 | 212 | # pylint: disable=W0221 | ||
141 | 213 | @staticmethod | ||
142 | 214 | def from_consumer_and_token(*args, **kwargs): | ||
143 | 215 | """A fake from_consumer_and_token.""" | ||
144 | 216 | fromcandt_call.append((args, kwargs)) | ||
145 | 217 | builder = original_oauthrequest.from_consumer_and_token | ||
146 | 218 | return builder(*args, **kwargs) | ||
147 | 219 | # pylint: enable=W0221 | ||
148 | 220 | |||
149 | 221 | self.patch(oauth, "OAuthRequest", FakeOAuthRequest) | ||
150 | 222 | |||
151 | 223 | authorizer = TimestampedAuthorizer(get_fake_timestamp, "ubuntuone") | ||
152 | 224 | authorizer.authorizeRequest(fake_uri, "POST", None, {}) | ||
153 | 225 | call_kwargs = fromcandt_call[0][1] | ||
154 | 226 | parameters = call_kwargs["parameters"] | ||
155 | 227 | self.assertEqual(parameters["oauth_timestamp"], fake_timestamp) | ||
156 | 228 | |||
157 | 229 | |||
158 | 196 | class SSOLoginProcessorTestCase(TestCase, MockerTestCase): | 230 | class SSOLoginProcessorTestCase(TestCase, MockerTestCase): |
159 | 197 | """Test suite for the SSO login processor.""" | 231 | """Test suite for the SSO login processor.""" |
160 | 198 | 232 | ||
161 | @@ -1426,6 +1460,8 @@ | |||
162 | 1426 | return FakedResponse(code=200) | 1460 | return FakedResponse(code=200) |
163 | 1427 | 1461 | ||
164 | 1428 | self.patch(urllib2, 'urlopen', fake_it) | 1462 | self.patch(urllib2, 'urlopen', fake_it) |
165 | 1463 | self.patch(ubuntu_sso.main.timestamp_checker, "get_faithful_time", | ||
166 | 1464 | time.time) | ||
167 | 1429 | 1465 | ||
168 | 1430 | self.client = SSOCredentials(None) | 1466 | self.client = SSOCredentials(None) |
169 | 1431 | 1467 | ||
170 | 1432 | 1468 | ||
171 | === added directory 'ubuntu_sso/utils' | |||
172 | === added file 'ubuntu_sso/utils/__init__.py' | |||
173 | --- ubuntu_sso/utils/__init__.py 1970-01-01 00:00:00 +0000 | |||
174 | +++ ubuntu_sso/utils/__init__.py 2011-11-23 19:50:27 +0000 | |||
175 | @@ -0,0 +1,81 @@ | |||
176 | 1 | # -*- coding: utf-8 -*- | ||
177 | 2 | |||
178 | 3 | # Author: Alejandro J. Cura <alecu@canonical.com> | ||
179 | 4 | # | ||
180 | 5 | # Copyright 2010, 2011 Canonical Ltd. | ||
181 | 6 | # | ||
182 | 7 | # This program is free software: you can redistribute it and/or modify it | ||
183 | 8 | # under the terms of the GNU General Public License version 3, as published | ||
184 | 9 | # by the Free Software Foundation. | ||
185 | 10 | # | ||
186 | 11 | # This program is distributed in the hope that it will be useful, but | ||
187 | 12 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
188 | 13 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
189 | 14 | # PURPOSE. See the GNU General Public License for more details. | ||
190 | 15 | # | ||
191 | 16 | # You should have received a copy of the GNU General Public License along | ||
192 | 17 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
193 | 18 | |||
194 | 19 | """Utility modules that may find use outside ubuntu_sso.""" | ||
195 | 20 | import time | ||
196 | 21 | import urllib2 | ||
197 | 22 | from twisted.web import http | ||
198 | 23 | |||
199 | 24 | from ubuntu_sso.logger import setup_logging | ||
200 | 25 | logger = setup_logging("ubuntu_sso.utils") | ||
201 | 26 | |||
202 | 27 | |||
203 | 28 | class RequestHead(urllib2.Request): | ||
204 | 29 | """A request with the method set to HEAD.""" | ||
205 | 30 | |||
206 | 31 | _request_method = "HEAD" | ||
207 | 32 | |||
208 | 33 | def get_method(self): | ||
209 | 34 | """Return the desired method.""" | ||
210 | 35 | return self._request_method | ||
211 | 36 | |||
212 | 37 | |||
213 | 38 | class SyncTimestampChecker(object): | ||
214 | 39 | """A timestamp that's regularly checked with a server.""" | ||
215 | 40 | |||
216 | 41 | CHECKING_INTERVAL = 60 * 60 # in seconds | ||
217 | 42 | ERROR_INTERVAL = 30 # in seconds | ||
218 | 43 | SERVER_URL = "http://one.ubuntu.com/api/time" | ||
219 | 44 | |||
220 | 45 | def __init__(self): | ||
221 | 46 | """Initialize this instance.""" | ||
222 | 47 | self.next_check = time.time() | ||
223 | 48 | self.skew = 0 | ||
224 | 49 | |||
225 | 50 | def get_server_time(self): | ||
226 | 51 | """Get the time at the server.""" | ||
227 | 52 | headers = {"Cache-Control": "no-cache"} | ||
228 | 53 | request = RequestHead(self.SERVER_URL, headers=headers) | ||
229 | 54 | response = urllib2.urlopen(request) | ||
230 | 55 | date_string = response.info()["Date"] | ||
231 | 56 | timestamp = http.stringToDatetime(date_string) | ||
232 | 57 | return timestamp | ||
233 | 58 | |||
234 | 59 | def get_faithful_time(self): | ||
235 | 60 | """Get an accurate timestamp.""" | ||
236 | 61 | local_time = time.time() | ||
237 | 62 | if local_time >= self.next_check: | ||
238 | 63 | try: | ||
239 | 64 | server_time = self.get_server_time() | ||
240 | 65 | self.next_check = local_time + self.CHECKING_INTERVAL | ||
241 | 66 | self.skew = server_time - local_time | ||
242 | 67 | logger.debug("Calculated server-local time skew: %r", | ||
243 | 68 | self.skew) | ||
244 | 69 | #pylint: disable=W0703 | ||
245 | 70 | except Exception, server_error: | ||
246 | 71 | logger.debug("Error while verifying the server time skew: %r", | ||
247 | 72 | server_error) | ||
248 | 73 | self.next_check = local_time + self.ERROR_INTERVAL | ||
249 | 74 | logger.debug("Using corrected timestamp: %r", | ||
250 | 75 | http.datetimeToString(local_time + self.skew)) | ||
251 | 76 | return int(local_time + self.skew) | ||
252 | 77 | |||
253 | 78 | |||
254 | 79 | # pylint: disable=C0103 | ||
255 | 80 | timestamp_checker = SyncTimestampChecker() | ||
256 | 81 | # pylint: enable=C0103 | ||
257 | 0 | 82 | ||
258 | === added directory 'ubuntu_sso/utils/tests' | |||
259 | === added file 'ubuntu_sso/utils/tests/__init__.py' | |||
260 | --- ubuntu_sso/utils/tests/__init__.py 1970-01-01 00:00:00 +0000 | |||
261 | +++ ubuntu_sso/utils/tests/__init__.py 2011-11-23 19:50:27 +0000 | |||
262 | @@ -0,0 +1,16 @@ | |||
263 | 1 | # ubuntu_sso - Ubuntu Single Sign On client support for desktop apps | ||
264 | 2 | # | ||
265 | 3 | # Copyright 2011 Canonical Ltd. | ||
266 | 4 | # | ||
267 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
268 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
269 | 7 | # by the Free Software Foundation. | ||
270 | 8 | # | ||
271 | 9 | # This program is distributed in the hope that it will be useful, but | ||
272 | 10 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
273 | 11 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
274 | 12 | # PURPOSE. See the GNU General Public License for more details. | ||
275 | 13 | # | ||
276 | 14 | # You should have received a copy of the GNU General Public License along | ||
277 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
278 | 16 | """Ubuntu Single Sign On utils tests.""" | ||
279 | 0 | 17 | ||
280 | === added file 'ubuntu_sso/utils/tests/test_oauth_headers.py' | |||
281 | --- ubuntu_sso/utils/tests/test_oauth_headers.py 1970-01-01 00:00:00 +0000 | |||
282 | +++ ubuntu_sso/utils/tests/test_oauth_headers.py 2011-11-23 19:50:27 +0000 | |||
283 | @@ -0,0 +1,148 @@ | |||
284 | 1 | # -*- coding: utf-8 -*- | ||
285 | 2 | |||
286 | 3 | # Author: Alejandro J. Cura <alecu@canonical.com> | ||
287 | 4 | # | ||
288 | 5 | # Copyright 2011 Canonical Ltd. | ||
289 | 6 | # | ||
290 | 7 | # This program is free software: you can redistribute it and/or modify it | ||
291 | 8 | # under the terms of the GNU General Public License version 3, as published | ||
292 | 9 | # by the Free Software Foundation. | ||
293 | 10 | # | ||
294 | 11 | # This program is distributed in the hope that it will be useful, but | ||
295 | 12 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
296 | 13 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
297 | 14 | # PURPOSE. See the GNU General Public License for more details. | ||
298 | 15 | # | ||
299 | 16 | # You should have received a copy of the GNU General Public License along | ||
300 | 17 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
301 | 18 | |||
302 | 19 | """Tests for the oauth_headers helper function.""" | ||
303 | 20 | |||
304 | 21 | import time | ||
305 | 22 | |||
306 | 23 | from twisted.application import internet, service | ||
307 | 24 | from twisted.internet import defer | ||
308 | 25 | from twisted.internet.threads import deferToThread | ||
309 | 26 | from twisted.trial.unittest import TestCase | ||
310 | 27 | from twisted.web import server, resource | ||
311 | 28 | |||
312 | 29 | from ubuntu_sso.utils import SyncTimestampChecker | ||
313 | 30 | |||
314 | 31 | |||
315 | 32 | class RootResource(resource.Resource): | ||
316 | 33 | """A root resource that logs the number of calls.""" | ||
317 | 34 | |||
318 | 35 | isLeaf = True | ||
319 | 36 | |||
320 | 37 | def __init__(self, *args, **kwargs): | ||
321 | 38 | """Initialize this fake instance.""" | ||
322 | 39 | resource.Resource.__init__(self, *args, **kwargs) | ||
323 | 40 | self.count = 0 | ||
324 | 41 | self.request_headers = [] | ||
325 | 42 | |||
326 | 43 | # pylint: disable=C0103 | ||
327 | 44 | def render_HEAD(self, request): | ||
328 | 45 | """Increase the counter on each render.""" | ||
329 | 46 | self.count += 1 | ||
330 | 47 | self.request_headers.append(request.requestHeaders) | ||
331 | 48 | return "" | ||
332 | 49 | |||
333 | 50 | |||
334 | 51 | class MockWebServer(object): | ||
335 | 52 | """A mock webserver for testing.""" | ||
336 | 53 | |||
337 | 54 | def __init__(self): | ||
338 | 55 | """Start up this instance.""" | ||
339 | 56 | # pylint: disable=E1101 | ||
340 | 57 | self.root = RootResource() | ||
341 | 58 | site = server.Site(self.root) | ||
342 | 59 | application = service.Application('web') | ||
343 | 60 | self.service_collection = service.IServiceCollection(application) | ||
344 | 61 | self.tcpserver = internet.TCPServer(0, site) | ||
345 | 62 | self.tcpserver.setServiceParent(self.service_collection) | ||
346 | 63 | self.service_collection.startService() | ||
347 | 64 | |||
348 | 65 | def get_url(self): | ||
349 | 66 | """Build the url for this mock server.""" | ||
350 | 67 | # pylint: disable=W0212 | ||
351 | 68 | port_num = self.tcpserver._port.getHost().port | ||
352 | 69 | return "http://localhost:%d/" % port_num | ||
353 | 70 | |||
354 | 71 | def stop(self): | ||
355 | 72 | """Shut it down.""" | ||
356 | 73 | # pylint: disable=E1101 | ||
357 | 74 | self.service_collection.stopService() | ||
358 | 75 | |||
359 | 76 | |||
360 | 77 | class FakedError(Exception): | ||
361 | 78 | """A mock, test, sample, and fake exception.""" | ||
362 | 79 | |||
363 | 80 | |||
364 | 81 | class TimestampCheckerTestCase(TestCase): | ||
365 | 82 | """Tests for the timestamp checker.""" | ||
366 | 83 | |||
367 | 84 | def setUp(self): | ||
368 | 85 | """Initialize a fake webserver.""" | ||
369 | 86 | self.ws = MockWebServer() | ||
370 | 87 | self.addCleanup(self.ws.stop) | ||
371 | 88 | self.patch(SyncTimestampChecker, "SERVER_URL", self.ws.get_url()) | ||
372 | 89 | |||
373 | 90 | @defer.inlineCallbacks | ||
374 | 91 | def test_returned_value_is_int(self): | ||
375 | 92 | """The returned value is an integer.""" | ||
376 | 93 | checker = SyncTimestampChecker() | ||
377 | 94 | timestamp = yield deferToThread(checker.get_faithful_time) | ||
378 | 95 | self.assertEqual(type(timestamp), int) | ||
379 | 96 | |||
380 | 97 | @defer.inlineCallbacks | ||
381 | 98 | def test_first_call_does_head(self): | ||
382 | 99 | """The first call gets the clock from our web.""" | ||
383 | 100 | checker = SyncTimestampChecker() | ||
384 | 101 | yield deferToThread(checker.get_faithful_time) | ||
385 | 102 | self.assertEqual(self.ws.root.count, 1) | ||
386 | 103 | |||
387 | 104 | @defer.inlineCallbacks | ||
388 | 105 | def test_second_call_is_cached(self): | ||
389 | 106 | """For the second call, the time is cached.""" | ||
390 | 107 | checker = SyncTimestampChecker() | ||
391 | 108 | yield deferToThread(checker.get_faithful_time) | ||
392 | 109 | yield deferToThread(checker.get_faithful_time) | ||
393 | 110 | self.assertEqual(self.ws.root.count, 1) | ||
394 | 111 | |||
395 | 112 | @defer.inlineCallbacks | ||
396 | 113 | def test_after_timeout_cache_expires(self): | ||
397 | 114 | """After some time, the cache expires.""" | ||
398 | 115 | fake_timestamp = 1 | ||
399 | 116 | self.patch(time, "time", lambda: fake_timestamp) | ||
400 | 117 | checker = SyncTimestampChecker() | ||
401 | 118 | yield deferToThread(checker.get_faithful_time) | ||
402 | 119 | fake_timestamp += SyncTimestampChecker.CHECKING_INTERVAL | ||
403 | 120 | yield deferToThread(checker.get_faithful_time) | ||
404 | 121 | self.assertEqual(self.ws.root.count, 2) | ||
405 | 122 | |||
406 | 123 | @defer.inlineCallbacks | ||
407 | 124 | def test_server_date_sends_nocache_headers(self): | ||
408 | 125 | """Getting the server date sends the no-cache headers.""" | ||
409 | 126 | checker = SyncTimestampChecker() | ||
410 | 127 | yield deferToThread(checker.get_server_time) | ||
411 | 128 | assert len(self.ws.root.request_headers) == 1 | ||
412 | 129 | headers = self.ws.root.request_headers[0] | ||
413 | 130 | result = headers.getRawHeaders("Cache-Control") | ||
414 | 131 | self.assertEqual(result, ["no-cache"]) | ||
415 | 132 | |||
416 | 133 | @defer.inlineCallbacks | ||
417 | 134 | def test_server_error_means_skew_not_updated(self): | ||
418 | 135 | """When server can't be reached, the skew is not updated.""" | ||
419 | 136 | fake_timestamp = 1 | ||
420 | 137 | self.patch(time, "time", lambda: fake_timestamp) | ||
421 | 138 | checker = SyncTimestampChecker() | ||
422 | 139 | |||
423 | 140 | def failing_get_server_time(): | ||
424 | 141 | """Let's fail while retrieving the server time.""" | ||
425 | 142 | raise FakedError() | ||
426 | 143 | |||
427 | 144 | self.patch(checker, "get_server_time", failing_get_server_time) | ||
428 | 145 | yield deferToThread(checker.get_faithful_time) | ||
429 | 146 | self.assertEqual(checker.skew, 0) | ||
430 | 147 | self.assertEqual(checker.next_check, | ||
431 | 148 | fake_timestamp + SyncTimestampChecker.ERROR_INTERVAL) |
Text conflict in bin/ubuntu- sso-login sso/gtk/ gui.py sso/gtk/ tests/test_ gui.py sso/keyring/ linux.py sso/keyring/ tests/test_ linux.py sso/main/ linux.py sso/main/ tests/test_ linux.py sso/utils. moved.
Text conflict in data/gtk/ui.glade
Text conflict in run-tests
Text conflict in setup.py
Text conflict in ubuntu_
Text conflict in ubuntu_
Text conflict in ubuntu_
Text conflict in ubuntu_
Text conflict in ubuntu_
Text conflict in ubuntu_
Conflict adding file ubuntu_sso/utils. Moved existing file to ubuntu_
11 conflicts encountered.