Merge lp:~jml/launchpad/extract-ssh-server-auth into lp:launchpad
- extract-ssh-server-auth
- Merge into devel
Proposed by
Jonathan Lange
Status: | Merged |
---|---|
Approved by: | Eleanor Berger |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~jml/launchpad/extract-ssh-server-auth |
Merge into: | lp:launchpad |
Prerequisite: | lp:~jml/launchpad/extract-ssh-server-logging |
Diff against target: |
620 lines (+252/-125) (has conflicts) 9 files modified
daemons/sftp.tac (+3/-2) lib/lp/codehosting/sshserver/auth.py (+17/-49) lib/lp/codehosting/sshserver/daemon.py (+105/-0) lib/lp/codehosting/sshserver/service.py (+19/-57) lib/lp/codehosting/sshserver/tests/test_auth.py (+7/-7) lib/lp/codehosting/sshserver/tests/test_daemon.py (+93/-0) lib/lp/codehosting/sshserver/tests/test_session.py (+4/-4) lib/lp/codehosting/tests/helpers.py (+0/-2) lib/lp/codehosting/tests/test_sftp.py (+4/-4) Text conflict in daemons/sftp.tac Text conflict in lib/lp/codehosting/sshserver/accesslog.py Text conflict in lib/lp/codehosting/sshserver/service.py Text conflict in lib/lp/codehosting/sshserver/tests/test_auth.py |
To merge this branch: | bzr merge lp:~jml/launchpad/extract-ssh-server-auth |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eleanor Berger (community) | code | Approve | |
Review via email: mp+23482@code.launchpad.net |
Commit message
Move codehosting-
Description of the change
This is yet another part of the massive branch that splits out the ssh server from codehosting.
This branch takes the codehosting-
Most of the changes are fairly simple. The only subtlety is that the LaunchpadAvatar class now has a new child class called CodehostingAvatar.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'daemons/sftp.tac' |
2 | --- daemons/sftp.tac 2010-04-16 19:00:51 +0000 |
3 | +++ daemons/sftp.tac 2010-04-16 19:00:53 +0000 |
4 | @@ -18,9 +18,10 @@ |
5 | # Construct an Application that has the codehosting SSH server. |
6 | ======= |
7 | |
8 | -from lp.codehosting.sshserver.service import ( |
9 | +from lp.codehosting.sshserver.daemon import ( |
10 | ACCESS_LOG_NAME, get_key_path, LOG_NAME, make_portal, OOPS_CONFIG_SECTION, |
11 | - PRIVATE_KEY_FILE, PUBLIC_KEY_FILE, SSHService) |
12 | + PRIVATE_KEY_FILE, PUBLIC_KEY_FILE) |
13 | +from lp.codehosting.sshserver.service import SSHService |
14 | |
15 | |
16 | # Construct an Application that has the codehosting SSH server. |
17 | |
18 | === modified file 'lib/lp/codehosting/sshserver/auth.py' |
19 | --- lib/lp/codehosting/sshserver/auth.py 2010-04-16 19:00:51 +0000 |
20 | +++ lib/lp/codehosting/sshserver/auth.py 2010-04-16 19:00:53 +0000 |
21 | @@ -13,7 +13,8 @@ |
22 | |
23 | __metaclass__ = type |
24 | __all__ = [ |
25 | - 'get_portal', |
26 | + 'LaunchpadAvatar', |
27 | + 'PublicKeyFromLaunchpadChecker', |
28 | 'SSHUserAuthServer', |
29 | ] |
30 | |
31 | @@ -21,48 +22,47 @@ |
32 | |
33 | from twisted.conch import avatar |
34 | from twisted.conch.error import ConchError |
35 | -from twisted.conch.interfaces import IConchUser, ISession |
36 | -from twisted.conch.ssh import filetransfer, keys, userauth |
37 | +from twisted.conch.interfaces import IConchUser |
38 | +from twisted.conch.ssh import keys, userauth |
39 | from twisted.conch.ssh.common import getNS, NS |
40 | from twisted.conch.checkers import SSHPublicKeyDatabase |
41 | |
42 | from twisted.cred.error import UnauthorizedLogin |
43 | from twisted.cred.checkers import ICredentialsChecker |
44 | from twisted.cred import credentials |
45 | -from twisted.cred.portal import IRealm, Portal |
46 | |
47 | from twisted.internet import defer |
48 | |
49 | -from twisted.python import components, failure |
50 | +from twisted.python import failure |
51 | |
52 | from zope.event import notify |
53 | from zope.interface import implements |
54 | |
55 | from lp.codehosting import sftp |
56 | from lp.codehosting.sshserver import events |
57 | -from lp.codehosting.sshserver.session import ( |
58 | - launch_smart_server, PatchedSSHSession) |
59 | +from lp.codehosting.sshserver.session import PatchedSSHSession |
60 | from lp.services.twistedsupport.xmlrpc import trap_fault |
61 | -from canonical.config import config |
62 | from canonical.launchpad.xmlrpc import faults |
63 | |
64 | |
65 | class LaunchpadAvatar(avatar.ConchUser): |
66 | """An account on the SSH server, corresponding to a Launchpad person. |
67 | |
68 | - :ivar branchfs_proxy: A Twisted XML-RPC client for the authserver. The |
69 | - server must implement `IBranchFileSystem`. |
70 | :ivar channelLookup: See `avatar.ConchUser`. |
71 | :ivar subsystemLookup: See `avatar.ConchUser`. |
72 | :ivar user_id: The Launchpad database ID of the Person for this account. |
73 | :ivar username: The Launchpad username for this account. |
74 | """ |
75 | |
76 | - def __init__(self, userDict, branchfs_proxy): |
77 | + def __init__(self, user_dict): |
78 | + """Construct a `LaunchpadAvatar`. |
79 | + |
80 | + :param user_dict: The result of a call to |
81 | + `IAuthServer.getUserAndSSHKeys`. |
82 | + """ |
83 | avatar.ConchUser.__init__(self) |
84 | - self.branchfs_proxy = branchfs_proxy |
85 | - self.user_id = userDict['id'] |
86 | - self.username = userDict['name'] |
87 | + self.user_id = user_dict['id'] |
88 | + self.username = user_dict['name'] |
89 | |
90 | # Set the only channel as a standard SSH session (with a couple of bug |
91 | # fixes). |
92 | @@ -74,34 +74,10 @@ |
93 | notify(events.UserLoggedOut(self)) |
94 | |
95 | |
96 | -components.registerAdapter(launch_smart_server, LaunchpadAvatar, ISession) |
97 | - |
98 | -components.registerAdapter( |
99 | - sftp.avatar_to_sftp_server, LaunchpadAvatar, filetransfer.ISFTPServer) |
100 | - |
101 | - |
102 | class UserDisplayedUnauthorizedLogin(UnauthorizedLogin): |
103 | """UnauthorizedLogin which should be reported to the user.""" |
104 | |
105 | |
106 | -class Realm: |
107 | - implements(IRealm) |
108 | - |
109 | - def __init__(self, authentication_proxy, branchfs_proxy): |
110 | - self.authentication_proxy = authentication_proxy |
111 | - self.branchfs_proxy = branchfs_proxy |
112 | - |
113 | - def requestAvatar(self, avatarId, mind, *interfaces): |
114 | - # Fetch the user's details from the authserver |
115 | - deferred = mind.lookupUserDetails(self.authentication_proxy, avatarId) |
116 | - |
117 | - # Once all those details are retrieved, we can construct the avatar. |
118 | - def gotUserDict(userDict): |
119 | - avatar = LaunchpadAvatar(userDict, self.branchfs_proxy) |
120 | - return interfaces[0], avatar, avatar.logout |
121 | - return deferred.addCallback(gotUserDict) |
122 | - |
123 | - |
124 | class ISSHPrivateKeyWithMind(credentials.ISSHPrivateKey): |
125 | """Marker interface for SSH credentials that reference a Mind.""" |
126 | |
127 | @@ -303,7 +279,7 @@ |
128 | raise UserDisplayedUnauthorizedLogin( |
129 | "No such Launchpad account: %s" % credentials.username) |
130 | |
131 | - def _checkForAuthorizedKey(self, userDict, credentials): |
132 | + def _checkForAuthorizedKey(self, user_dict, credentials): |
133 | """Check the key data in credentials against the keys found in LP.""" |
134 | if credentials.algName == 'ssh-dss': |
135 | wantKeyType = 'DSA' |
136 | @@ -313,12 +289,12 @@ |
137 | # unknown key type |
138 | return False |
139 | |
140 | - if len(userDict['keys']) == 0: |
141 | + if len(user_dict['keys']) == 0: |
142 | raise UserDisplayedUnauthorizedLogin( |
143 | "Launchpad user %r doesn't have a registered SSH key" |
144 | % credentials.username) |
145 | |
146 | - for keytype, keytext in userDict['keys']: |
147 | + for keytype, keytext in user_dict['keys']: |
148 | if keytype != wantKeyType: |
149 | continue |
150 | try: |
151 | @@ -330,11 +306,3 @@ |
152 | raise UnauthorizedLogin( |
153 | "Your SSH key does not match any key registered for Launchpad " |
154 | "user %s" % credentials.username) |
155 | - |
156 | - |
157 | -def get_portal(authentication_proxy, branchfs_proxy): |
158 | - """Get a portal for connecting to Launchpad codehosting.""" |
159 | - portal = Portal(Realm(authentication_proxy, branchfs_proxy)) |
160 | - portal.registerChecker( |
161 | - PublicKeyFromLaunchpadChecker(authentication_proxy)) |
162 | - return portal |
163 | |
164 | === added file 'lib/lp/codehosting/sshserver/daemon.py' |
165 | --- lib/lp/codehosting/sshserver/daemon.py 1970-01-01 00:00:00 +0000 |
166 | +++ lib/lp/codehosting/sshserver/daemon.py 2010-04-16 19:00:53 +0000 |
167 | @@ -0,0 +1,105 @@ |
168 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
169 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
170 | + |
171 | +"""Glues the codehosting SSH daemon together.""" |
172 | + |
173 | +__metaclass__ = type |
174 | +__all__ = [ |
175 | + 'ACCESS_LOG_NAME', |
176 | + 'CodehostingAvatar', |
177 | + 'get_key_path', |
178 | + 'get_portal', |
179 | + 'LOG_NAME', |
180 | + 'make_portal', |
181 | + 'PRIVATE_KEY_FILE', |
182 | + 'PUBLIC_KEY_FILE', |
183 | + ] |
184 | + |
185 | +import os |
186 | + |
187 | +from twisted.conch.interfaces import ISession |
188 | +from twisted.conch.ssh import filetransfer |
189 | +from twisted.cred.portal import IRealm, Portal |
190 | +from twisted.python import components |
191 | +from twisted.web.xmlrpc import Proxy |
192 | + |
193 | +from zope.interface import implements |
194 | + |
195 | +from canonical.config import config |
196 | +from lp.codehosting import sftp |
197 | +from lp.codehosting.sshserver.auth import ( |
198 | + LaunchpadAvatar, PublicKeyFromLaunchpadChecker) |
199 | +from lp.codehosting.sshserver.session import launch_smart_server |
200 | + |
201 | + |
202 | +# The names of the key files of the server itself. The directory itself is |
203 | +# given in config.codehosting.host_key_pair_path. |
204 | +PRIVATE_KEY_FILE = 'ssh_host_key_rsa' |
205 | +PUBLIC_KEY_FILE = 'ssh_host_key_rsa.pub' |
206 | + |
207 | +OOPS_CONFIG_SECTION = 'codehosting' |
208 | +LOG_NAME = 'codehosting' |
209 | +ACCESS_LOG_NAME = 'codehosting.access' |
210 | + |
211 | + |
212 | +class CodehostingAvatar(LaunchpadAvatar): |
213 | + """An SSH avatar specific to codehosting. |
214 | + |
215 | + :ivar branchfs_proxy: A Twisted XML-RPC client for the authserver. The |
216 | + server must implement `IBranchFileSystem`. |
217 | + """ |
218 | + |
219 | + def __init__(self, user_dict, branchfs_proxy): |
220 | + LaunchpadAvatar.__init__(self, user_dict) |
221 | + self.branchfs_proxy = branchfs_proxy |
222 | + |
223 | + |
224 | +components.registerAdapter(launch_smart_server, CodehostingAvatar, ISession) |
225 | + |
226 | +components.registerAdapter( |
227 | + sftp.avatar_to_sftp_server, CodehostingAvatar, filetransfer.ISFTPServer) |
228 | + |
229 | + |
230 | +class Realm: |
231 | + implements(IRealm) |
232 | + |
233 | + def __init__(self, authentication_proxy, branchfs_proxy): |
234 | + self.authentication_proxy = authentication_proxy |
235 | + self.branchfs_proxy = branchfs_proxy |
236 | + |
237 | + def requestAvatar(self, avatar_id, mind, *interfaces): |
238 | + # Fetch the user's details from the authserver |
239 | + deferred = mind.lookupUserDetails( |
240 | + self.authentication_proxy, avatar_id) |
241 | + |
242 | + # Once all those details are retrieved, we can construct the avatar. |
243 | + def got_user_dict(user_dict): |
244 | + avatar = CodehostingAvatar(user_dict, self.branchfs_proxy) |
245 | + return interfaces[0], avatar, avatar.logout |
246 | + |
247 | + return deferred.addCallback(got_user_dict) |
248 | + |
249 | + |
250 | +def get_portal(authentication_proxy, branchfs_proxy): |
251 | + """Get a portal for connecting to Launchpad codehosting.""" |
252 | + portal = Portal(Realm(authentication_proxy, branchfs_proxy)) |
253 | + portal.registerChecker( |
254 | + PublicKeyFromLaunchpadChecker(authentication_proxy)) |
255 | + return portal |
256 | + |
257 | + |
258 | +def get_key_path(key_filename): |
259 | + key_directory = config.codehosting.host_key_pair_path |
260 | + return os.path.join(config.root, key_directory, key_filename) |
261 | + |
262 | + |
263 | +def make_portal(): |
264 | + """Create and return a `Portal` for the SSH service. |
265 | + |
266 | + This portal accepts SSH credentials and returns our customized SSH |
267 | + avatars (see `lp.codehosting.sshserver.auth.CodehostingAvatar`). |
268 | + """ |
269 | + authentication_proxy = Proxy( |
270 | + config.codehosting.authentication_endpoint) |
271 | + branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint) |
272 | + return get_portal(authentication_proxy, branchfs_proxy) |
273 | |
274 | === modified file 'lib/lp/codehosting/sshserver/service.py' |
275 | --- lib/lp/codehosting/sshserver/service.py 2010-04-16 19:00:51 +0000 |
276 | +++ lib/lp/codehosting/sshserver/service.py 2010-04-16 19:00:53 +0000 |
277 | @@ -8,12 +8,6 @@ |
278 | |
279 | __metaclass__ = type |
280 | __all__ = [ |
281 | - 'ACCESS_LOG_NAME', |
282 | - 'get_key_path', |
283 | - 'LOG_NAME', |
284 | - 'make_portal', |
285 | - 'PRIVATE_KEY_FILE', |
286 | - 'PUBLIC_KEY_FILE', |
287 | 'SSHService', |
288 | ] |
289 | |
290 | @@ -27,13 +21,11 @@ |
291 | from twisted.conch.ssh.transport import SSHServerTransport |
292 | from twisted.internet import defer |
293 | from twisted.protocols.policies import TimeoutFactory |
294 | -from twisted.web.xmlrpc import Proxy |
295 | |
296 | from zope.event import notify |
297 | |
298 | -from canonical.config import config |
299 | from lp.codehosting.sshserver import accesslog, events |
300 | -from lp.codehosting.sshserver.auth import get_portal, SSHUserAuthServer |
301 | +from lp.codehosting.sshserver.auth import SSHUserAuthServer |
302 | from lp.services.twistedsupport import gatherResults |
303 | from lp.services.twistedsupport.loggingsupport import set_up_oops_reporting |
304 | |
305 | @@ -43,16 +35,6 @@ |
306 | PRIVATE_KEY_FILE = 'ssh_host_key_rsa' |
307 | PUBLIC_KEY_FILE = 'ssh_host_key_rsa.pub' |
308 | |
309 | -OOPS_CONFIG_SECTION = 'codehosting' |
310 | -LOG_NAME = 'codehosting' |
311 | -ACCESS_LOG_NAME = 'codehosting.access' |
312 | - |
313 | - |
314 | -# The names of the key files of the server itself. The directory itself is |
315 | -# given in config.codehosting.host_key_pair_path. |
316 | -PRIVATE_KEY_FILE = 'ssh_host_key_rsa' |
317 | -PUBLIC_KEY_FILE = 'ssh_host_key_rsa.pub' |
318 | - |
319 | |
320 | class KeepAliveSettingSSHServerTransport(SSHServerTransport): |
321 | |
322 | @@ -61,44 +43,24 @@ |
323 | self.transport.setTcpKeepAlive(True) |
324 | |
325 | |
326 | -<<<<<<< TREE |
327 | -def get_key_path(key_filename): |
328 | - key_directory = config.codehosting.host_key_pair_path |
329 | - return os.path.join(config.root, key_directory, key_filename) |
330 | - |
331 | - |
332 | -def make_portal(): |
333 | - """Create and return a `Portal` for the SSH service. |
334 | - |
335 | - This portal accepts SSH credentials and returns our customized SSH |
336 | - avatars (see `lp.codehosting.sshserver.auth.LaunchpadAvatar`). |
337 | - """ |
338 | - authentication_proxy = Proxy( |
339 | - config.codehosting.authentication_endpoint) |
340 | - branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint) |
341 | - return get_portal(authentication_proxy, branchfs_proxy) |
342 | - |
343 | - |
344 | - |
345 | -======= |
346 | -def get_key_path(key_filename): |
347 | - key_directory = config.codehosting.host_key_pair_path |
348 | - return os.path.join(config.root, key_directory, key_filename) |
349 | - |
350 | - |
351 | -def make_portal(): |
352 | - """Create and return a `Portal` for the SSH service. |
353 | - |
354 | - This portal accepts SSH credentials and returns our customized SSH |
355 | - avatars (see `lp.codehosting.sshserver.auth.LaunchpadAvatar`). |
356 | - """ |
357 | - authentication_proxy = Proxy( |
358 | - config.codehosting.authentication_endpoint) |
359 | - branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint) |
360 | - return get_portal(authentication_proxy, branchfs_proxy) |
361 | - |
362 | - |
363 | ->>>>>>> MERGE-SOURCE |
364 | +def get_key_path(key_filename): |
365 | + key_directory = config.codehosting.host_key_pair_path |
366 | + return os.path.join(config.root, key_directory, key_filename) |
367 | + |
368 | + |
369 | +def make_portal(): |
370 | + """Create and return a `Portal` for the SSH service. |
371 | + |
372 | + This portal accepts SSH credentials and returns our customized SSH |
373 | + avatars (see `lp.codehosting.sshserver.auth.LaunchpadAvatar`). |
374 | + """ |
375 | + authentication_proxy = Proxy( |
376 | + config.codehosting.authentication_endpoint) |
377 | + branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint) |
378 | + return get_portal(authentication_proxy, branchfs_proxy) |
379 | + |
380 | + |
381 | + |
382 | class Factory(SSHFactory): |
383 | """SSH factory that uses Launchpad's custom authentication. |
384 | |
385 | |
386 | === modified file 'lib/lp/codehosting/sshserver/tests/test_auth.py' |
387 | --- lib/lp/codehosting/sshserver/tests/test_auth.py 2010-04-13 18:32:15 +0000 |
388 | +++ lib/lp/codehosting/sshserver/tests/test_auth.py 2010-04-16 19:00:53 +0000 |
389 | @@ -18,14 +18,13 @@ |
390 | from twisted.internet import defer |
391 | from twisted.python import failure |
392 | from twisted.python.util import sibpath |
393 | -from twisted.test.proto_helpers import StringTransport |
394 | |
395 | from twisted.trial.unittest import TestCase as TrialTestCase |
396 | |
397 | from canonical.config import config |
398 | from canonical.launchpad.xmlrpc import faults |
399 | from canonical.testing.layers import TwistedLayer |
400 | -from lp.codehosting.sshserver import auth, service |
401 | +from lp.codehosting.sshserver import auth |
402 | from lp.services.twistedsupport import suppress_stderr |
403 | |
404 | |
405 | @@ -38,14 +37,12 @@ |
406 | |
407 | implements(IRealm) |
408 | |
409 | - def requestAvatar(self, avatarId, mind, *interfaces): |
410 | + def requestAvatar(self, avatar_id, mind, *interfaces): |
411 | user_dict = { |
412 | - 'id': avatarId, 'name': avatarId, 'teams': [], |
413 | + 'id': avatar_id, 'name': avatar_id, 'teams': [], |
414 | 'initialBranches': []} |
415 | return ( |
416 | - interfaces[0], |
417 | - auth.LaunchpadAvatar(user_dict, None), |
418 | - lambda: None) |
419 | + interfaces[0], auth.LaunchpadAvatar(user_dict), lambda: None) |
420 | |
421 | |
422 | class MockSSHTransport(SSHServerTransport): |
423 | @@ -501,6 +498,7 @@ |
424 | return d |
425 | |
426 | |
427 | +<<<<<<< TREE |
428 | class StringTransportWith_setTcpKeepAlive(StringTransport): |
429 | def __init__(self, hostAddress=None, peerAddress=None): |
430 | StringTransport.__init__(self, hostAddress, peerAddress) |
431 | @@ -569,5 +567,7 @@ |
432 | |
433 | self.assertNotIdentical(mind1.cache, mind2.cache) |
434 | |
435 | +======= |
436 | +>>>>>>> MERGE-SOURCE |
437 | def test_suite(): |
438 | return unittest.TestLoader().loadTestsFromName(__name__) |
439 | |
440 | === added file 'lib/lp/codehosting/sshserver/tests/test_daemon.py' |
441 | --- lib/lp/codehosting/sshserver/tests/test_daemon.py 1970-01-01 00:00:00 +0000 |
442 | +++ lib/lp/codehosting/sshserver/tests/test_daemon.py 2010-04-16 19:00:53 +0000 |
443 | @@ -0,0 +1,93 @@ |
444 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
445 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
446 | + |
447 | +"""Tests for the codehosting SSH server glue.""" |
448 | + |
449 | +__metaclass__ = type |
450 | + |
451 | +import unittest |
452 | + |
453 | +from twisted.conch.ssh.common import NS |
454 | +from twisted.conch.ssh.keys import Key |
455 | +from twisted.test.proto_helpers import StringTransport |
456 | +from twisted.trial.unittest import TestCase as TrialTestCase |
457 | + |
458 | +from canonical.testing.layers import TwistedLayer |
459 | + |
460 | +from lp.codehosting.sshserver.auth import SSHUserAuthServer |
461 | +from lp.codehosting.sshserver.daemon import ( |
462 | + get_key_path, get_portal, PRIVATE_KEY_FILE, PUBLIC_KEY_FILE) |
463 | +from lp.codehosting.sshserver.service import Factory |
464 | + |
465 | + |
466 | +class StringTransportWith_setTcpKeepAlive(StringTransport): |
467 | + def __init__(self, hostAddress=None, peerAddress=None): |
468 | + StringTransport.__init__(self, hostAddress, peerAddress) |
469 | + self._keepAlive = False |
470 | + |
471 | + def setTcpKeepAlive(self, flag): |
472 | + self._keepAlive = flag |
473 | + |
474 | + |
475 | +class TestFactory(TrialTestCase): |
476 | + """Tests for our SSH factory.""" |
477 | + |
478 | + layer = TwistedLayer |
479 | + |
480 | + def makeFactory(self): |
481 | + """Create and start the factory that our SSH server uses.""" |
482 | + factory = Factory( |
483 | + get_portal(None, None), |
484 | + private_key=Key.fromFile( |
485 | + get_key_path(PRIVATE_KEY_FILE)), |
486 | + public_key=Key.fromFile( |
487 | + get_key_path(PUBLIC_KEY_FILE))) |
488 | + factory.startFactory() |
489 | + return factory |
490 | + |
491 | + def startConnecting(self, factory): |
492 | + """Connect to the `factory`.""" |
493 | + server_transport = factory.buildProtocol(None) |
494 | + server_transport.makeConnection(StringTransportWith_setTcpKeepAlive()) |
495 | + return server_transport |
496 | + |
497 | + def test_set_keepalive_on_connection(self): |
498 | + # The server transport sets TCP keep alives on the underlying |
499 | + # transport. |
500 | + factory = self.makeFactory() |
501 | + server_transport = self.startConnecting(factory) |
502 | + self.assertTrue(server_transport.transport._keepAlive) |
503 | + |
504 | + def beginAuthentication(self, factory): |
505 | + """Connect to `factory` and begin authentication on this connection. |
506 | + |
507 | + :return: The `SSHServerTransport` after the process of authentication |
508 | + has begun. |
509 | + """ |
510 | + server_transport = self.startConnecting(factory) |
511 | + server_transport.ssh_SERVICE_REQUEST(NS('ssh-userauth')) |
512 | + self.addCleanup(server_transport.service.serviceStopped) |
513 | + return server_transport |
514 | + |
515 | + def test_authentication_uses_our_userauth_service(self): |
516 | + # The service of a SSHServerTransport after authentication has started |
517 | + # is an instance of our SSHUserAuthServer class. |
518 | + factory = self.makeFactory() |
519 | + transport = self.beginAuthentication(factory) |
520 | + self.assertIsInstance(transport.service, SSHUserAuthServer) |
521 | + |
522 | + def test_two_connections_two_minds(self): |
523 | + # Two attempts to authenticate do not share the user-details cache. |
524 | + factory = self.makeFactory() |
525 | + |
526 | + server_transport1 = self.beginAuthentication(factory) |
527 | + server_transport2 = self.beginAuthentication(factory) |
528 | + |
529 | + mind1 = server_transport1.service.getMind() |
530 | + mind2 = server_transport2.service.getMind() |
531 | + |
532 | + self.assertNotIdentical(mind1.cache, mind2.cache) |
533 | + |
534 | + |
535 | +def test_suite(): |
536 | + return unittest.TestLoader().loadTestsFromName(__name__) |
537 | |
538 | === modified file 'lib/lp/codehosting/sshserver/tests/test_session.py' |
539 | --- lib/lp/codehosting/sshserver/tests/test_session.py 2010-03-15 06:42:34 +0000 |
540 | +++ lib/lp/codehosting/sshserver/tests/test_session.py 2010-04-16 19:00:53 +0000 |
541 | @@ -14,7 +14,7 @@ |
542 | |
543 | from canonical.config import config |
544 | from lp.codehosting import get_bzr_path, get_BZR_PLUGIN_PATH_for_subprocess |
545 | -from lp.codehosting.sshserver.auth import LaunchpadAvatar |
546 | +from lp.codehosting.sshserver.daemon import CodehostingAvatar |
547 | from lp.codehosting.sshserver.session import ( |
548 | ExecOnlySession, ForbiddenCommand, RestrictedExecOnlySession) |
549 | from lp.codehosting.tests.helpers import AvatarTestCase |
550 | @@ -83,7 +83,7 @@ |
551 | |
552 | def setUp(self): |
553 | AvatarTestCase.setUp(self) |
554 | - self.avatar = LaunchpadAvatar(self.aliceUserDict, None) |
555 | + self.avatar = CodehostingAvatar(self.aliceUserDict, None) |
556 | # The logging system will try to get the id of avatar.transport, so |
557 | # let's give it something to take the id of. |
558 | self.avatar.transport = object() |
559 | @@ -239,7 +239,7 @@ |
560 | |
561 | def setUp(self): |
562 | AvatarTestCase.setUp(self) |
563 | - self.avatar = LaunchpadAvatar(self.aliceUserDict, None) |
564 | + self.avatar = CodehostingAvatar(self.aliceUserDict, None) |
565 | self.reactor = MockReactor() |
566 | self.session = RestrictedExecOnlySession( |
567 | self.avatar, self.reactor, 'foo', 'bar baz %(user_id)s') |
568 | @@ -305,7 +305,7 @@ |
569 | |
570 | def setUp(self): |
571 | AvatarTestCase.setUp(self) |
572 | - self.avatar = LaunchpadAvatar(self.aliceUserDict, None) |
573 | + self.avatar = CodehostingAvatar(self.aliceUserDict, None) |
574 | |
575 | def test_avatarAdaptsToRestrictedExecOnlySession(self): |
576 | # When Conch tries to adapt the SSH server avatar to ISession, it |
577 | |
578 | === modified file 'lib/lp/codehosting/tests/helpers.py' |
579 | --- lib/lp/codehosting/tests/helpers.py 2009-06-25 04:06:00 +0000 |
580 | +++ lib/lp/codehosting/tests/helpers.py 2010-04-16 19:00:53 +0000 |
581 | @@ -7,9 +7,7 @@ |
582 | __all__ = [ |
583 | 'AvatarTestCase', |
584 | 'adapt_suite', |
585 | - 'BranchTestCase', |
586 | 'CodeHostingTestProviderAdapter', |
587 | - 'CodeHostingRepositoryTestProviderAdapter', |
588 | 'create_branch_with_one_revision', |
589 | 'deferToThread', |
590 | 'LoomTestMixin', |
591 | |
592 | === modified file 'lib/lp/codehosting/tests/test_sftp.py' |
593 | --- lib/lp/codehosting/tests/test_sftp.py 2010-02-01 04:33:22 +0000 |
594 | +++ lib/lp/codehosting/tests/test_sftp.py 2010-04-16 19:00:53 +0000 |
595 | @@ -25,7 +25,7 @@ |
596 | from lp.codehosting.inmemory import InMemoryFrontend, XMLRPCWrapper |
597 | from lp.codehosting.sftp import ( |
598 | FatLocalTransport, TransportSFTPServer, FileIsADirectory) |
599 | -from lp.codehosting.sshserver.auth import LaunchpadAvatar |
600 | +from lp.codehosting.sshserver.daemon import CodehostingAvatar |
601 | from lp.testing.factory import LaunchpadObjectFactory |
602 | from canonical.testing.layers import TwistedLayer |
603 | |
604 | @@ -110,13 +110,13 @@ |
605 | self.branchfs_endpoint = XMLRPCWrapper( |
606 | frontend.getFilesystemEndpoint()) |
607 | |
608 | - def makeLaunchpadAvatar(self): |
609 | + def makeCodehostingAvatar(self): |
610 | user = self.factory.makePerson() |
611 | user_dict = dict(id=user.id, name=user.name) |
612 | - return LaunchpadAvatar(user_dict, self.branchfs_endpoint) |
613 | + return CodehostingAvatar(user_dict, self.branchfs_endpoint) |
614 | |
615 | def test_canAdaptToSFTPServer(self): |
616 | - avatar = self.makeLaunchpadAvatar() |
617 | + avatar = self.makeCodehostingAvatar() |
618 | # The adapter logs the SFTPStarted event, which gets the id of the |
619 | # transport attribute of 'avatar'. Here we set transport to an |
620 | # arbitrary object that can have its id taken. |
There were a few places where moved legacy code didn't conform to our stringent coding conventions, and you kindly offered to fix them before landing this branch.