mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-06-02 15:50:54 +00:00
SFTP: implement execCommand to interoperate with clients that issue a 'df -P -k /' command. Also eliminate use of Zope adaptation.
This commit is contained in:
parent
6ba6c97a7a
commit
8028bf01bc
@ -16,6 +16,7 @@ from twisted.conch.interfaces import ISFTPServer, ISFTPFile, IConchUser, ISessio
|
|||||||
from twisted.conch.avatar import ConchUser
|
from twisted.conch.avatar import ConchUser
|
||||||
from twisted.conch.openssh_compat import primes
|
from twisted.conch.openssh_compat import primes
|
||||||
from twisted.cred import portal
|
from twisted.cred import portal
|
||||||
|
from twisted.internet.error import ProcessDone, ProcessTerminated
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from twisted.internet.interfaces import IFinishableConsumer
|
from twisted.internet.interfaces import IFinishableConsumer
|
||||||
@ -786,6 +787,7 @@ class GeneralSFTPFile(PrefixingLogMixin):
|
|||||||
self.async.addCallbacks(_resize, eventually_errback(d))
|
self.async.addCallbacks(_resize, eventually_errback(d))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
class StoppableList:
|
class StoppableList:
|
||||||
def __init__(self, items):
|
def __init__(self, items):
|
||||||
self.items = items
|
self.items = items
|
||||||
@ -796,17 +798,63 @@ class StoppableList:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SFTPHandler(PrefixingLogMixin):
|
class Reason:
|
||||||
implements(ISFTPServer)
|
def __init__(self, value):
|
||||||
def __init__(self, user):
|
self.value = value
|
||||||
PrefixingLogMixin.__init__(self, facility="tahoe.sftp")
|
|
||||||
if noisy: self.log(".__init__(%r)" % (user,), level=NOISY)
|
|
||||||
|
|
||||||
self.check_abort = user.check_abort
|
|
||||||
self.client = user.client
|
class SFTPUserHandler(ConchUser, PrefixingLogMixin):
|
||||||
self.root = user.root
|
implements(ISFTPServer, ISession)
|
||||||
self.username = user.username
|
def __init__(self, check_abort, client, rootnode, username):
|
||||||
self.convergence = user.convergence
|
ConchUser.__init__(self)
|
||||||
|
PrefixingLogMixin.__init__(self, facility="tahoe.sftp")
|
||||||
|
if noisy: self.log(".__init__(%r, %r, %r, %r)" %
|
||||||
|
(check_abort, client, rootnode, username), level=NOISY)
|
||||||
|
|
||||||
|
self.channelLookup["session"] = session.SSHSession
|
||||||
|
self.subsystemLookup["sftp"] = FileTransferServer
|
||||||
|
|
||||||
|
self.client = client
|
||||||
|
self.root = rootnode
|
||||||
|
self.username = username
|
||||||
|
self.convergence = client.convergence
|
||||||
|
self.logged_out = False
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
self.logged_out = True
|
||||||
|
|
||||||
|
def check_abort(self):
|
||||||
|
return self.logged_out
|
||||||
|
|
||||||
|
# ISession
|
||||||
|
# This is needed because some clients may try to issue a 'df' command.
|
||||||
|
|
||||||
|
def getPty(self, terminal, windowSize, attrs):
|
||||||
|
self.log(".getPty(%r, %r, %r)" % (terminal, windowSize, attrs), level=OPERATIONAL)
|
||||||
|
|
||||||
|
def openShell(self, protocol):
|
||||||
|
self.log(".openShell(%r)" % (protocol,), level=OPERATIONAL)
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def execCommand(self, protocol, cmd):
|
||||||
|
self.log(".execCommand(%r, %r)" % (protocol, cmd), level=OPERATIONAL)
|
||||||
|
if cmd == "df -P -k /":
|
||||||
|
protocol.write("Filesystem 1024-blocks Used Available Capacity Mounted on\n"
|
||||||
|
"tahoe 628318530 314159265 314159265 50% /\n")
|
||||||
|
protocol.processEnded(Reason(ProcessDone(None)))
|
||||||
|
else:
|
||||||
|
protocol.processEnded(Reason(ProcessTerminated(exitCode=1)))
|
||||||
|
|
||||||
|
def windowChanged(self, newWindowSize):
|
||||||
|
self.log(".windowChanged(%r)" % (newWindowSize,), level=OPERATIONAL)
|
||||||
|
|
||||||
|
def eofReceived(self):
|
||||||
|
self.log(".eofReceived()", level=OPERATIONAL)
|
||||||
|
|
||||||
|
def closed(self):
|
||||||
|
self.log(".closed()", level=OPERATIONAL)
|
||||||
|
|
||||||
|
# ISFTPServer
|
||||||
|
|
||||||
def gotVersion(self, otherVersion, extData):
|
def gotVersion(self, otherVersion, extData):
|
||||||
self.log(".gotVersion(%r, %r)" % (otherVersion, extData), level=OPERATIONAL)
|
self.log(".gotVersion(%r, %r)" % (otherVersion, extData), level=OPERATIONAL)
|
||||||
@ -1262,14 +1310,9 @@ class Dispatcher:
|
|||||||
def requestAvatar(self, avatarID, mind, interface):
|
def requestAvatar(self, avatarID, mind, interface):
|
||||||
assert interface == IConchUser
|
assert interface == IConchUser
|
||||||
rootnode = self.client.create_node_from_uri(avatarID.rootcap)
|
rootnode = self.client.create_node_from_uri(avatarID.rootcap)
|
||||||
convergence = self.client.convergence
|
handler = SFTPUserHandler(self.client, rootnode, avatarID.username)
|
||||||
logged_out = {'flag': False}
|
return (interface, handler, handler.logout)
|
||||||
def check_abort():
|
|
||||||
return logged_out['flag']
|
|
||||||
def logout():
|
|
||||||
logged_out['flag'] = True
|
|
||||||
s = SFTPUser(check_abort, self.client, rootnode, avatarID.username, convergence)
|
|
||||||
return (interface, s, logout)
|
|
||||||
|
|
||||||
class SFTPServer(service.MultiService):
|
class SFTPServer(service.MultiService):
|
||||||
def __init__(self, client, accountfile, accounturl,
|
def __init__(self, client, accountfile, accounturl,
|
||||||
@ -1287,7 +1330,7 @@ class SFTPServer(service.MultiService):
|
|||||||
p.registerChecker(c)
|
p.registerChecker(c)
|
||||||
if not accountfile and not accounturl:
|
if not accountfile and not accounturl:
|
||||||
# we could leave this anonymous, with just the /uri/CAP form
|
# we could leave this anonymous, with just the /uri/CAP form
|
||||||
raise NeedRootcapLookupScheme("must provide some translation")
|
raise NeedRootcapLookupScheme("must provide an account file or URL")
|
||||||
|
|
||||||
pubkey = keys.Key.fromFile(pubkey_file)
|
pubkey = keys.Key.fromFile(pubkey_file)
|
||||||
privkey = keys.Key.fromFile(privkey_file)
|
privkey = keys.Key.fromFile(privkey_file)
|
||||||
|
@ -5,6 +5,7 @@ from stat import S_IFREG, S_IFDIR
|
|||||||
from twisted.trial import unittest
|
from twisted.trial import unittest
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from twisted.python.failure import Failure
|
from twisted.python.failure import Failure
|
||||||
|
from twisted.internet.error import ProcessDone, ProcessTerminated
|
||||||
|
|
||||||
sftp = None
|
sftp = None
|
||||||
sftpd = None
|
sftpd = None
|
||||||
@ -89,18 +90,15 @@ class Handler(GridTestMixin, ShouldFailMixin, unittest.TestCase):
|
|||||||
self.basedir = "sftp/" + basedir
|
self.basedir = "sftp/" + basedir
|
||||||
self.set_up_grid(num_clients=num_clients, num_servers=num_servers)
|
self.set_up_grid(num_clients=num_clients, num_servers=num_servers)
|
||||||
|
|
||||||
def check_abort():
|
self.check_abort = lambda: False
|
||||||
pass
|
|
||||||
self.client = self.g.clients[0]
|
self.client = self.g.clients[0]
|
||||||
self.username = "alice"
|
self.username = "alice"
|
||||||
self.convergence = "convergence"
|
|
||||||
|
|
||||||
d = self.client.create_dirnode()
|
d = self.client.create_dirnode()
|
||||||
def _created_root(node):
|
def _created_root(node):
|
||||||
self.root = node
|
self.root = node
|
||||||
self.root_uri = node.get_uri()
|
self.root_uri = node.get_uri()
|
||||||
self.user = sftpd.SFTPUser(check_abort, self.client, self.root, self.username, self.convergence)
|
self.handler = sftpd.SFTPUserHandler(self.check_abort, self.client, self.root, self.username)
|
||||||
self.handler = sftpd.SFTPHandler(self.user)
|
|
||||||
d.addCallback(_created_root)
|
d.addCallback(_created_root)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@ -915,3 +913,31 @@ class Handler(GridTestMixin, ShouldFailMixin, unittest.TestCase):
|
|||||||
self.shouldFailWithSFTPError(sftp.FX_PERMISSION_DENIED, "makeDirectory small",
|
self.shouldFailWithSFTPError(sftp.FX_PERMISSION_DENIED, "makeDirectory small",
|
||||||
self.handler.makeDirectory, "small", {}))
|
self.handler.makeDirectory, "small", {}))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
def test_execCommand(self):
|
||||||
|
class FakeProtocol:
|
||||||
|
def __init__(self):
|
||||||
|
self.output = ""
|
||||||
|
self.reason = None
|
||||||
|
def write(self, data):
|
||||||
|
self.output += data
|
||||||
|
def processEnded(self, reason):
|
||||||
|
self.reason = reason
|
||||||
|
|
||||||
|
protocol_ok = FakeProtocol()
|
||||||
|
protocol_error = FakeProtocol()
|
||||||
|
|
||||||
|
d = self._set_up("execCommand")
|
||||||
|
|
||||||
|
d.addCallback(lambda ign: self.handler.execCommand(protocol_ok, "df -P -k /"))
|
||||||
|
d.addCallback(lambda ign: self.failUnlessIn("1024-blocks", protocol_ok.output))
|
||||||
|
d.addCallback(lambda ign: self.failUnless(isinstance(protocol_ok.reason.value, ProcessDone)))
|
||||||
|
d.addCallback(lambda ign: self.handler.eofReceived())
|
||||||
|
d.addCallback(lambda ign: self.handler.closed())
|
||||||
|
|
||||||
|
d.addCallback(lambda ign: self.handler.execCommand(protocol_error, "error"))
|
||||||
|
d.addCallback(lambda ign: self.failUnlessEqual(protocol_error.output, ""))
|
||||||
|
d.addCallback(lambda ign: self.failUnless(isinstance(protocol_error.reason.value, ProcessTerminated)))
|
||||||
|
d.addCallback(lambda ign: self.failUnlessEqual(protocol_error.reason.value.exitCode, 1))
|
||||||
|
|
||||||
|
return d
|
Loading…
x
Reference in New Issue
Block a user