Merge lp:~jml/launchpad/extract-ssh-server-auth into lp:launchpad

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
Reviewer Review Type Date Requested Status
Eleanor Berger (community) code Approve
Review via email: mp+23482@code.launchpad.net

Commit message

Move codehosting-specific stuff into new codehosting-specific module

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-specific auth stuff and puts it into a new module: lp.codehosting.sshserver.daemon. It also takes the codehosting-specific stuff from the "service" module and puts it in there.

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.
Revision history for this message
Eleanor Berger (intellectronica) wrote :

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.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'daemons/sftp.tac'
--- daemons/sftp.tac 2010-04-16 19:00:51 +0000
+++ daemons/sftp.tac 2010-04-16 19:00:53 +0000
@@ -18,9 +18,10 @@
18# Construct an Application that has the codehosting SSH server.18# Construct an Application that has the codehosting SSH server.
19=======19=======
2020
21from lp.codehosting.sshserver.service import (21from lp.codehosting.sshserver.daemon import (
22 ACCESS_LOG_NAME, get_key_path, LOG_NAME, make_portal, OOPS_CONFIG_SECTION,22 ACCESS_LOG_NAME, get_key_path, LOG_NAME, make_portal, OOPS_CONFIG_SECTION,
23 PRIVATE_KEY_FILE, PUBLIC_KEY_FILE, SSHService)23 PRIVATE_KEY_FILE, PUBLIC_KEY_FILE)
24from lp.codehosting.sshserver.service import SSHService
2425
2526
26# Construct an Application that has the codehosting SSH server.27# Construct an Application that has the codehosting SSH server.
2728
=== modified file 'lib/lp/codehosting/sshserver/auth.py'
--- lib/lp/codehosting/sshserver/auth.py 2010-04-16 19:00:51 +0000
+++ lib/lp/codehosting/sshserver/auth.py 2010-04-16 19:00:53 +0000
@@ -13,7 +13,8 @@
1313
14__metaclass__ = type14__metaclass__ = type
15__all__ = [15__all__ = [
16 'get_portal',16 'LaunchpadAvatar',
17 'PublicKeyFromLaunchpadChecker',
17 'SSHUserAuthServer',18 'SSHUserAuthServer',
18 ]19 ]
1920
@@ -21,48 +22,47 @@
2122
22from twisted.conch import avatar23from twisted.conch import avatar
23from twisted.conch.error import ConchError24from twisted.conch.error import ConchError
24from twisted.conch.interfaces import IConchUser, ISession25from twisted.conch.interfaces import IConchUser
25from twisted.conch.ssh import filetransfer, keys, userauth26from twisted.conch.ssh import keys, userauth
26from twisted.conch.ssh.common import getNS, NS27from twisted.conch.ssh.common import getNS, NS
27from twisted.conch.checkers import SSHPublicKeyDatabase28from twisted.conch.checkers import SSHPublicKeyDatabase
2829
29from twisted.cred.error import UnauthorizedLogin30from twisted.cred.error import UnauthorizedLogin
30from twisted.cred.checkers import ICredentialsChecker31from twisted.cred.checkers import ICredentialsChecker
31from twisted.cred import credentials32from twisted.cred import credentials
32from twisted.cred.portal import IRealm, Portal
3333
34from twisted.internet import defer34from twisted.internet import defer
3535
36from twisted.python import components, failure36from twisted.python import failure
3737
38from zope.event import notify38from zope.event import notify
39from zope.interface import implements39from zope.interface import implements
4040
41from lp.codehosting import sftp41from lp.codehosting import sftp
42from lp.codehosting.sshserver import events42from lp.codehosting.sshserver import events
43from lp.codehosting.sshserver.session import (43from lp.codehosting.sshserver.session import PatchedSSHSession
44 launch_smart_server, PatchedSSHSession)
45from lp.services.twistedsupport.xmlrpc import trap_fault44from lp.services.twistedsupport.xmlrpc import trap_fault
46from canonical.config import config
47from canonical.launchpad.xmlrpc import faults45from canonical.launchpad.xmlrpc import faults
4846
4947
50class LaunchpadAvatar(avatar.ConchUser):48class LaunchpadAvatar(avatar.ConchUser):
51 """An account on the SSH server, corresponding to a Launchpad person.49 """An account on the SSH server, corresponding to a Launchpad person.
5250
53 :ivar branchfs_proxy: A Twisted XML-RPC client for the authserver. The
54 server must implement `IBranchFileSystem`.
55 :ivar channelLookup: See `avatar.ConchUser`.51 :ivar channelLookup: See `avatar.ConchUser`.
56 :ivar subsystemLookup: See `avatar.ConchUser`.52 :ivar subsystemLookup: See `avatar.ConchUser`.
57 :ivar user_id: The Launchpad database ID of the Person for this account.53 :ivar user_id: The Launchpad database ID of the Person for this account.
58 :ivar username: The Launchpad username for this account.54 :ivar username: The Launchpad username for this account.
59 """55 """
6056
61 def __init__(self, userDict, branchfs_proxy):57 def __init__(self, user_dict):
58 """Construct a `LaunchpadAvatar`.
59
60 :param user_dict: The result of a call to
61 `IAuthServer.getUserAndSSHKeys`.
62 """
62 avatar.ConchUser.__init__(self)63 avatar.ConchUser.__init__(self)
63 self.branchfs_proxy = branchfs_proxy64 self.user_id = user_dict['id']
64 self.user_id = userDict['id']65 self.username = user_dict['name']
65 self.username = userDict['name']
6666
67 # Set the only channel as a standard SSH session (with a couple of bug67 # Set the only channel as a standard SSH session (with a couple of bug
68 # fixes).68 # fixes).
@@ -74,34 +74,10 @@
74 notify(events.UserLoggedOut(self))74 notify(events.UserLoggedOut(self))
7575
7676
77components.registerAdapter(launch_smart_server, LaunchpadAvatar, ISession)
78
79components.registerAdapter(
80 sftp.avatar_to_sftp_server, LaunchpadAvatar, filetransfer.ISFTPServer)
81
82
83class UserDisplayedUnauthorizedLogin(UnauthorizedLogin):77class UserDisplayedUnauthorizedLogin(UnauthorizedLogin):
84 """UnauthorizedLogin which should be reported to the user."""78 """UnauthorizedLogin which should be reported to the user."""
8579
8680
87class Realm:
88 implements(IRealm)
89
90 def __init__(self, authentication_proxy, branchfs_proxy):
91 self.authentication_proxy = authentication_proxy
92 self.branchfs_proxy = branchfs_proxy
93
94 def requestAvatar(self, avatarId, mind, *interfaces):
95 # Fetch the user's details from the authserver
96 deferred = mind.lookupUserDetails(self.authentication_proxy, avatarId)
97
98 # Once all those details are retrieved, we can construct the avatar.
99 def gotUserDict(userDict):
100 avatar = LaunchpadAvatar(userDict, self.branchfs_proxy)
101 return interfaces[0], avatar, avatar.logout
102 return deferred.addCallback(gotUserDict)
103
104
105class ISSHPrivateKeyWithMind(credentials.ISSHPrivateKey):81class ISSHPrivateKeyWithMind(credentials.ISSHPrivateKey):
106 """Marker interface for SSH credentials that reference a Mind."""82 """Marker interface for SSH credentials that reference a Mind."""
10783
@@ -303,7 +279,7 @@
303 raise UserDisplayedUnauthorizedLogin(279 raise UserDisplayedUnauthorizedLogin(
304 "No such Launchpad account: %s" % credentials.username)280 "No such Launchpad account: %s" % credentials.username)
305281
306 def _checkForAuthorizedKey(self, userDict, credentials):282 def _checkForAuthorizedKey(self, user_dict, credentials):
307 """Check the key data in credentials against the keys found in LP."""283 """Check the key data in credentials against the keys found in LP."""
308 if credentials.algName == 'ssh-dss':284 if credentials.algName == 'ssh-dss':
309 wantKeyType = 'DSA'285 wantKeyType = 'DSA'
@@ -313,12 +289,12 @@
313 # unknown key type289 # unknown key type
314 return False290 return False
315291
316 if len(userDict['keys']) == 0:292 if len(user_dict['keys']) == 0:
317 raise UserDisplayedUnauthorizedLogin(293 raise UserDisplayedUnauthorizedLogin(
318 "Launchpad user %r doesn't have a registered SSH key"294 "Launchpad user %r doesn't have a registered SSH key"
319 % credentials.username)295 % credentials.username)
320296
321 for keytype, keytext in userDict['keys']:297 for keytype, keytext in user_dict['keys']:
322 if keytype != wantKeyType:298 if keytype != wantKeyType:
323 continue299 continue
324 try:300 try:
@@ -330,11 +306,3 @@
330 raise UnauthorizedLogin(306 raise UnauthorizedLogin(
331 "Your SSH key does not match any key registered for Launchpad "307 "Your SSH key does not match any key registered for Launchpad "
332 "user %s" % credentials.username)308 "user %s" % credentials.username)
333
334
335def get_portal(authentication_proxy, branchfs_proxy):
336 """Get a portal for connecting to Launchpad codehosting."""
337 portal = Portal(Realm(authentication_proxy, branchfs_proxy))
338 portal.registerChecker(
339 PublicKeyFromLaunchpadChecker(authentication_proxy))
340 return portal
341309
=== added file 'lib/lp/codehosting/sshserver/daemon.py'
--- lib/lp/codehosting/sshserver/daemon.py 1970-01-01 00:00:00 +0000
+++ lib/lp/codehosting/sshserver/daemon.py 2010-04-16 19:00:53 +0000
@@ -0,0 +1,105 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Glues the codehosting SSH daemon together."""
5
6__metaclass__ = type
7__all__ = [
8 'ACCESS_LOG_NAME',
9 'CodehostingAvatar',
10 'get_key_path',
11 'get_portal',
12 'LOG_NAME',
13 'make_portal',
14 'PRIVATE_KEY_FILE',
15 'PUBLIC_KEY_FILE',
16 ]
17
18import os
19
20from twisted.conch.interfaces import ISession
21from twisted.conch.ssh import filetransfer
22from twisted.cred.portal import IRealm, Portal
23from twisted.python import components
24from twisted.web.xmlrpc import Proxy
25
26from zope.interface import implements
27
28from canonical.config import config
29from lp.codehosting import sftp
30from lp.codehosting.sshserver.auth import (
31 LaunchpadAvatar, PublicKeyFromLaunchpadChecker)
32from lp.codehosting.sshserver.session import launch_smart_server
33
34
35# The names of the key files of the server itself. The directory itself is
36# given in config.codehosting.host_key_pair_path.
37PRIVATE_KEY_FILE = 'ssh_host_key_rsa'
38PUBLIC_KEY_FILE = 'ssh_host_key_rsa.pub'
39
40OOPS_CONFIG_SECTION = 'codehosting'
41LOG_NAME = 'codehosting'
42ACCESS_LOG_NAME = 'codehosting.access'
43
44
45class CodehostingAvatar(LaunchpadAvatar):
46 """An SSH avatar specific to codehosting.
47
48 :ivar branchfs_proxy: A Twisted XML-RPC client for the authserver. The
49 server must implement `IBranchFileSystem`.
50 """
51
52 def __init__(self, user_dict, branchfs_proxy):
53 LaunchpadAvatar.__init__(self, user_dict)
54 self.branchfs_proxy = branchfs_proxy
55
56
57components.registerAdapter(launch_smart_server, CodehostingAvatar, ISession)
58
59components.registerAdapter(
60 sftp.avatar_to_sftp_server, CodehostingAvatar, filetransfer.ISFTPServer)
61
62
63class Realm:
64 implements(IRealm)
65
66 def __init__(self, authentication_proxy, branchfs_proxy):
67 self.authentication_proxy = authentication_proxy
68 self.branchfs_proxy = branchfs_proxy
69
70 def requestAvatar(self, avatar_id, mind, *interfaces):
71 # Fetch the user's details from the authserver
72 deferred = mind.lookupUserDetails(
73 self.authentication_proxy, avatar_id)
74
75 # Once all those details are retrieved, we can construct the avatar.
76 def got_user_dict(user_dict):
77 avatar = CodehostingAvatar(user_dict, self.branchfs_proxy)
78 return interfaces[0], avatar, avatar.logout
79
80 return deferred.addCallback(got_user_dict)
81
82
83def get_portal(authentication_proxy, branchfs_proxy):
84 """Get a portal for connecting to Launchpad codehosting."""
85 portal = Portal(Realm(authentication_proxy, branchfs_proxy))
86 portal.registerChecker(
87 PublicKeyFromLaunchpadChecker(authentication_proxy))
88 return portal
89
90
91def get_key_path(key_filename):
92 key_directory = config.codehosting.host_key_pair_path
93 return os.path.join(config.root, key_directory, key_filename)
94
95
96def make_portal():
97 """Create and return a `Portal` for the SSH service.
98
99 This portal accepts SSH credentials and returns our customized SSH
100 avatars (see `lp.codehosting.sshserver.auth.CodehostingAvatar`).
101 """
102 authentication_proxy = Proxy(
103 config.codehosting.authentication_endpoint)
104 branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint)
105 return get_portal(authentication_proxy, branchfs_proxy)
0106
=== modified file 'lib/lp/codehosting/sshserver/service.py'
--- lib/lp/codehosting/sshserver/service.py 2010-04-16 19:00:51 +0000
+++ lib/lp/codehosting/sshserver/service.py 2010-04-16 19:00:53 +0000
@@ -8,12 +8,6 @@
88
9__metaclass__ = type9__metaclass__ = type
10__all__ = [10__all__ = [
11 'ACCESS_LOG_NAME',
12 'get_key_path',
13 'LOG_NAME',
14 'make_portal',
15 'PRIVATE_KEY_FILE',
16 'PUBLIC_KEY_FILE',
17 'SSHService',11 'SSHService',
18 ]12 ]
1913
@@ -27,13 +21,11 @@
27from twisted.conch.ssh.transport import SSHServerTransport21from twisted.conch.ssh.transport import SSHServerTransport
28from twisted.internet import defer22from twisted.internet import defer
29from twisted.protocols.policies import TimeoutFactory23from twisted.protocols.policies import TimeoutFactory
30from twisted.web.xmlrpc import Proxy
3124
32from zope.event import notify25from zope.event import notify
3326
34from canonical.config import config
35from lp.codehosting.sshserver import accesslog, events27from lp.codehosting.sshserver import accesslog, events
36from lp.codehosting.sshserver.auth import get_portal, SSHUserAuthServer28from lp.codehosting.sshserver.auth import SSHUserAuthServer
37from lp.services.twistedsupport import gatherResults29from lp.services.twistedsupport import gatherResults
38from lp.services.twistedsupport.loggingsupport import set_up_oops_reporting30from lp.services.twistedsupport.loggingsupport import set_up_oops_reporting
3931
@@ -43,16 +35,6 @@
43PRIVATE_KEY_FILE = 'ssh_host_key_rsa'35PRIVATE_KEY_FILE = 'ssh_host_key_rsa'
44PUBLIC_KEY_FILE = 'ssh_host_key_rsa.pub'36PUBLIC_KEY_FILE = 'ssh_host_key_rsa.pub'
4537
46OOPS_CONFIG_SECTION = 'codehosting'
47LOG_NAME = 'codehosting'
48ACCESS_LOG_NAME = 'codehosting.access'
49
50
51# The names of the key files of the server itself. The directory itself is
52# given in config.codehosting.host_key_pair_path.
53PRIVATE_KEY_FILE = 'ssh_host_key_rsa'
54PUBLIC_KEY_FILE = 'ssh_host_key_rsa.pub'
55
5638
57class KeepAliveSettingSSHServerTransport(SSHServerTransport):39class KeepAliveSettingSSHServerTransport(SSHServerTransport):
5840
@@ -61,44 +43,24 @@
61 self.transport.setTcpKeepAlive(True)43 self.transport.setTcpKeepAlive(True)
6244
6345
64<<<<<<< TREE46def get_key_path(key_filename):
65def get_key_path(key_filename):47 key_directory = config.codehosting.host_key_pair_path
66 key_directory = config.codehosting.host_key_pair_path48 return os.path.join(config.root, key_directory, key_filename)
67 return os.path.join(config.root, key_directory, key_filename)49
6850
6951def make_portal():
70def make_portal():52 """Create and return a `Portal` for the SSH service.
71 """Create and return a `Portal` for the SSH service.53
7254 This portal accepts SSH credentials and returns our customized SSH
73 This portal accepts SSH credentials and returns our customized SSH55 avatars (see `lp.codehosting.sshserver.auth.LaunchpadAvatar`).
74 avatars (see `lp.codehosting.sshserver.auth.LaunchpadAvatar`).56 """
75 """57 authentication_proxy = Proxy(
76 authentication_proxy = Proxy(58 config.codehosting.authentication_endpoint)
77 config.codehosting.authentication_endpoint)59 branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint)
78 branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint)60 return get_portal(authentication_proxy, branchfs_proxy)
79 return get_portal(authentication_proxy, branchfs_proxy)61
8062
8163
82
83=======
84def get_key_path(key_filename):
85 key_directory = config.codehosting.host_key_pair_path
86 return os.path.join(config.root, key_directory, key_filename)
87
88
89def make_portal():
90 """Create and return a `Portal` for the SSH service.
91
92 This portal accepts SSH credentials and returns our customized SSH
93 avatars (see `lp.codehosting.sshserver.auth.LaunchpadAvatar`).
94 """
95 authentication_proxy = Proxy(
96 config.codehosting.authentication_endpoint)
97 branchfs_proxy = Proxy(config.codehosting.branchfs_endpoint)
98 return get_portal(authentication_proxy, branchfs_proxy)
99
100
101>>>>>>> MERGE-SOURCE
102class Factory(SSHFactory):64class Factory(SSHFactory):
103 """SSH factory that uses Launchpad's custom authentication.65 """SSH factory that uses Launchpad's custom authentication.
10466
10567
=== modified file 'lib/lp/codehosting/sshserver/tests/test_auth.py'
--- lib/lp/codehosting/sshserver/tests/test_auth.py 2010-04-13 18:32:15 +0000
+++ lib/lp/codehosting/sshserver/tests/test_auth.py 2010-04-16 19:00:53 +0000
@@ -18,14 +18,13 @@
18from twisted.internet import defer18from twisted.internet import defer
19from twisted.python import failure19from twisted.python import failure
20from twisted.python.util import sibpath20from twisted.python.util import sibpath
21from twisted.test.proto_helpers import StringTransport
2221
23from twisted.trial.unittest import TestCase as TrialTestCase22from twisted.trial.unittest import TestCase as TrialTestCase
2423
25from canonical.config import config24from canonical.config import config
26from canonical.launchpad.xmlrpc import faults25from canonical.launchpad.xmlrpc import faults
27from canonical.testing.layers import TwistedLayer26from canonical.testing.layers import TwistedLayer
28from lp.codehosting.sshserver import auth, service27from lp.codehosting.sshserver import auth
29from lp.services.twistedsupport import suppress_stderr28from lp.services.twistedsupport import suppress_stderr
3029
3130
@@ -38,14 +37,12 @@
3837
39 implements(IRealm)38 implements(IRealm)
4039
41 def requestAvatar(self, avatarId, mind, *interfaces):40 def requestAvatar(self, avatar_id, mind, *interfaces):
42 user_dict = {41 user_dict = {
43 'id': avatarId, 'name': avatarId, 'teams': [],42 'id': avatar_id, 'name': avatar_id, 'teams': [],
44 'initialBranches': []}43 'initialBranches': []}
45 return (44 return (
46 interfaces[0],45 interfaces[0], auth.LaunchpadAvatar(user_dict), lambda: None)
47 auth.LaunchpadAvatar(user_dict, None),
48 lambda: None)
4946
5047
51class MockSSHTransport(SSHServerTransport):48class MockSSHTransport(SSHServerTransport):
@@ -501,6 +498,7 @@
501 return d498 return d
502499
503500
501<<<<<<< TREE
504class StringTransportWith_setTcpKeepAlive(StringTransport):502class StringTransportWith_setTcpKeepAlive(StringTransport):
505 def __init__(self, hostAddress=None, peerAddress=None):503 def __init__(self, hostAddress=None, peerAddress=None):
506 StringTransport.__init__(self, hostAddress, peerAddress)504 StringTransport.__init__(self, hostAddress, peerAddress)
@@ -569,5 +567,7 @@
569567
570 self.assertNotIdentical(mind1.cache, mind2.cache)568 self.assertNotIdentical(mind1.cache, mind2.cache)
571569
570=======
571>>>>>>> MERGE-SOURCE
572def test_suite():572def test_suite():
573 return unittest.TestLoader().loadTestsFromName(__name__)573 return unittest.TestLoader().loadTestsFromName(__name__)
574574
=== added file 'lib/lp/codehosting/sshserver/tests/test_daemon.py'
--- lib/lp/codehosting/sshserver/tests/test_daemon.py 1970-01-01 00:00:00 +0000
+++ lib/lp/codehosting/sshserver/tests/test_daemon.py 2010-04-16 19:00:53 +0000
@@ -0,0 +1,93 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for the codehosting SSH server glue."""
5
6__metaclass__ = type
7
8import unittest
9
10from twisted.conch.ssh.common import NS
11from twisted.conch.ssh.keys import Key
12from twisted.test.proto_helpers import StringTransport
13from twisted.trial.unittest import TestCase as TrialTestCase
14
15from canonical.testing.layers import TwistedLayer
16
17from lp.codehosting.sshserver.auth import SSHUserAuthServer
18from lp.codehosting.sshserver.daemon import (
19 get_key_path, get_portal, PRIVATE_KEY_FILE, PUBLIC_KEY_FILE)
20from lp.codehosting.sshserver.service import Factory
21
22
23class StringTransportWith_setTcpKeepAlive(StringTransport):
24 def __init__(self, hostAddress=None, peerAddress=None):
25 StringTransport.__init__(self, hostAddress, peerAddress)
26 self._keepAlive = False
27
28 def setTcpKeepAlive(self, flag):
29 self._keepAlive = flag
30
31
32class TestFactory(TrialTestCase):
33 """Tests for our SSH factory."""
34
35 layer = TwistedLayer
36
37 def makeFactory(self):
38 """Create and start the factory that our SSH server uses."""
39 factory = Factory(
40 get_portal(None, None),
41 private_key=Key.fromFile(
42 get_key_path(PRIVATE_KEY_FILE)),
43 public_key=Key.fromFile(
44 get_key_path(PUBLIC_KEY_FILE)))
45 factory.startFactory()
46 return factory
47
48 def startConnecting(self, factory):
49 """Connect to the `factory`."""
50 server_transport = factory.buildProtocol(None)
51 server_transport.makeConnection(StringTransportWith_setTcpKeepAlive())
52 return server_transport
53
54 def test_set_keepalive_on_connection(self):
55 # The server transport sets TCP keep alives on the underlying
56 # transport.
57 factory = self.makeFactory()
58 server_transport = self.startConnecting(factory)
59 self.assertTrue(server_transport.transport._keepAlive)
60
61 def beginAuthentication(self, factory):
62 """Connect to `factory` and begin authentication on this connection.
63
64 :return: The `SSHServerTransport` after the process of authentication
65 has begun.
66 """
67 server_transport = self.startConnecting(factory)
68 server_transport.ssh_SERVICE_REQUEST(NS('ssh-userauth'))
69 self.addCleanup(server_transport.service.serviceStopped)
70 return server_transport
71
72 def test_authentication_uses_our_userauth_service(self):
73 # The service of a SSHServerTransport after authentication has started
74 # is an instance of our SSHUserAuthServer class.
75 factory = self.makeFactory()
76 transport = self.beginAuthentication(factory)
77 self.assertIsInstance(transport.service, SSHUserAuthServer)
78
79 def test_two_connections_two_minds(self):
80 # Two attempts to authenticate do not share the user-details cache.
81 factory = self.makeFactory()
82
83 server_transport1 = self.beginAuthentication(factory)
84 server_transport2 = self.beginAuthentication(factory)
85
86 mind1 = server_transport1.service.getMind()
87 mind2 = server_transport2.service.getMind()
88
89 self.assertNotIdentical(mind1.cache, mind2.cache)
90
91
92def test_suite():
93 return unittest.TestLoader().loadTestsFromName(__name__)
094
=== modified file 'lib/lp/codehosting/sshserver/tests/test_session.py'
--- lib/lp/codehosting/sshserver/tests/test_session.py 2010-03-15 06:42:34 +0000
+++ lib/lp/codehosting/sshserver/tests/test_session.py 2010-04-16 19:00:53 +0000
@@ -14,7 +14,7 @@
1414
15from canonical.config import config15from canonical.config import config
16from lp.codehosting import get_bzr_path, get_BZR_PLUGIN_PATH_for_subprocess16from lp.codehosting import get_bzr_path, get_BZR_PLUGIN_PATH_for_subprocess
17from lp.codehosting.sshserver.auth import LaunchpadAvatar17from lp.codehosting.sshserver.daemon import CodehostingAvatar
18from lp.codehosting.sshserver.session import (18from lp.codehosting.sshserver.session import (
19 ExecOnlySession, ForbiddenCommand, RestrictedExecOnlySession)19 ExecOnlySession, ForbiddenCommand, RestrictedExecOnlySession)
20from lp.codehosting.tests.helpers import AvatarTestCase20from lp.codehosting.tests.helpers import AvatarTestCase
@@ -83,7 +83,7 @@
8383
84 def setUp(self):84 def setUp(self):
85 AvatarTestCase.setUp(self)85 AvatarTestCase.setUp(self)
86 self.avatar = LaunchpadAvatar(self.aliceUserDict, None)86 self.avatar = CodehostingAvatar(self.aliceUserDict, None)
87 # The logging system will try to get the id of avatar.transport, so87 # The logging system will try to get the id of avatar.transport, so
88 # let's give it something to take the id of.88 # let's give it something to take the id of.
89 self.avatar.transport = object()89 self.avatar.transport = object()
@@ -239,7 +239,7 @@
239239
240 def setUp(self):240 def setUp(self):
241 AvatarTestCase.setUp(self)241 AvatarTestCase.setUp(self)
242 self.avatar = LaunchpadAvatar(self.aliceUserDict, None)242 self.avatar = CodehostingAvatar(self.aliceUserDict, None)
243 self.reactor = MockReactor()243 self.reactor = MockReactor()
244 self.session = RestrictedExecOnlySession(244 self.session = RestrictedExecOnlySession(
245 self.avatar, self.reactor, 'foo', 'bar baz %(user_id)s')245 self.avatar, self.reactor, 'foo', 'bar baz %(user_id)s')
@@ -305,7 +305,7 @@
305305
306 def setUp(self):306 def setUp(self):
307 AvatarTestCase.setUp(self)307 AvatarTestCase.setUp(self)
308 self.avatar = LaunchpadAvatar(self.aliceUserDict, None)308 self.avatar = CodehostingAvatar(self.aliceUserDict, None)
309309
310 def test_avatarAdaptsToRestrictedExecOnlySession(self):310 def test_avatarAdaptsToRestrictedExecOnlySession(self):
311 # When Conch tries to adapt the SSH server avatar to ISession, it311 # When Conch tries to adapt the SSH server avatar to ISession, it
312312
=== modified file 'lib/lp/codehosting/tests/helpers.py'
--- lib/lp/codehosting/tests/helpers.py 2009-06-25 04:06:00 +0000
+++ lib/lp/codehosting/tests/helpers.py 2010-04-16 19:00:53 +0000
@@ -7,9 +7,7 @@
7__all__ = [7__all__ = [
8 'AvatarTestCase',8 'AvatarTestCase',
9 'adapt_suite',9 'adapt_suite',
10 'BranchTestCase',
11 'CodeHostingTestProviderAdapter',10 'CodeHostingTestProviderAdapter',
12 'CodeHostingRepositoryTestProviderAdapter',
13 'create_branch_with_one_revision',11 'create_branch_with_one_revision',
14 'deferToThread',12 'deferToThread',
15 'LoomTestMixin',13 'LoomTestMixin',
1614
=== modified file 'lib/lp/codehosting/tests/test_sftp.py'
--- lib/lp/codehosting/tests/test_sftp.py 2010-02-01 04:33:22 +0000
+++ lib/lp/codehosting/tests/test_sftp.py 2010-04-16 19:00:53 +0000
@@ -25,7 +25,7 @@
25from lp.codehosting.inmemory import InMemoryFrontend, XMLRPCWrapper25from lp.codehosting.inmemory import InMemoryFrontend, XMLRPCWrapper
26from lp.codehosting.sftp import (26from lp.codehosting.sftp import (
27 FatLocalTransport, TransportSFTPServer, FileIsADirectory)27 FatLocalTransport, TransportSFTPServer, FileIsADirectory)
28from lp.codehosting.sshserver.auth import LaunchpadAvatar28from lp.codehosting.sshserver.daemon import CodehostingAvatar
29from lp.testing.factory import LaunchpadObjectFactory29from lp.testing.factory import LaunchpadObjectFactory
30from canonical.testing.layers import TwistedLayer30from canonical.testing.layers import TwistedLayer
3131
@@ -110,13 +110,13 @@
110 self.branchfs_endpoint = XMLRPCWrapper(110 self.branchfs_endpoint = XMLRPCWrapper(
111 frontend.getFilesystemEndpoint())111 frontend.getFilesystemEndpoint())
112112
113 def makeLaunchpadAvatar(self):113 def makeCodehostingAvatar(self):
114 user = self.factory.makePerson()114 user = self.factory.makePerson()
115 user_dict = dict(id=user.id, name=user.name)115 user_dict = dict(id=user.id, name=user.name)
116 return LaunchpadAvatar(user_dict, self.branchfs_endpoint)116 return CodehostingAvatar(user_dict, self.branchfs_endpoint)
117117
118 def test_canAdaptToSFTPServer(self):118 def test_canAdaptToSFTPServer(self):
119 avatar = self.makeLaunchpadAvatar()119 avatar = self.makeCodehostingAvatar()
120 # The adapter logs the SFTPStarted event, which gets the id of the120 # The adapter logs the SFTPStarted event, which gets the id of the
121 # transport attribute of 'avatar'. Here we set transport to an121 # transport attribute of 'avatar'. Here we set transport to an
122 # arbitrary object that can have its id taken.122 # arbitrary object that can have its id taken.