[Zope3-checkins]
SVN: Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/
fixed a number of the error codes returned by the FTP server
and also fixed / added more tests
Michael Kerrin
michael.kerrin at openapp.biz
Fri Oct 7 18:10:56 EDT 2005
Log message for revision 38910:
fixed a number of the error codes returned by the FTP server and also fixed / added more tests
Changed:
U Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/ftp.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/test/test_zope_ftp.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/demofs.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_demofs.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_publisher.py
-=-
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/ftp.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/ftp.py 2005-10-07 21:13:08 UTC (rev 38909)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/ftp.py 2005-10-07 22:10:56 UTC (rev 38910)
@@ -20,6 +20,9 @@
from zope.interface import implements
+from zope.publisher.interfaces import NotFound
+from zope.security.interfaces import Unauthorized
+
from twisted.internet import threads, defer
from twisted.protocols import ftp
@@ -58,27 +61,52 @@
return '/' + '/'.join(path)
def makeDirectory(self, path):
- return threads.deferToThread(self.fs_access.mkdir, self._path(path))
+ def failed(failure):
+ raise ftp.PermissionDeniedError(self._path(path))
+ d = threads.deferToThread(self.fs_access.mkdir, self._path(path))
+ d.addErrback(failed)
+ return d
+
def removeDirectory(self, path):
- return threads.deferToThread(self.fs_access.rmdir, self._path(path))
+ def failed(failure):
+ raise ftp.PermissionDeniedError(self._path(path))
+ d = threads.deferToThread(self.fs_access.rmdir, self._path(path))
+ d.addErrback(failed)
+ return d
+
def removeFile(self, path):
- return threads.deferToThread(self.fs_access.remove, self._path(path))
+ def failed(failure):
+ raise ftp.PermissionDeniedError(self._path(path))
+ d = threads.deferToThread(self.fs_access.remove, self._path(path))
+ d.addErrback(failed)
+ return d
+
def rename(self, fromPath, toPath):
- return threads.deferToThread(self.fs_access.rename,
- self._path(fromPath),
- self._path(toPath))
+ def failed(failure):
+ raise ftp.PermissionDeniedError(self._path(path))
+ d = threads.deferToThread(self.fs_access.rename,
+ self._path(fromPath),
+ self._path(toPath))
+ d.addErrback(failed)
+ return d
+
def access(self, path):
def success(result):
return None
- def failure(result):
- ## XXX - return more appropriate results
- raise ftp.PermissionDeniedError(path)
- ## XXX - is ls the right method to use here - seems a bit slow.
+ def failure(failure):
+ if failure.type is NotFound:
+ raise ftp.FileNotFoundError(self._path(path))
+ ## Unauthorized error - is there any other errors I should
+ ## be catching.
+ raise ftp.PermissionDeniedError(self._path(path))
+
+ ## the ls method used where might be a bit slow on a directory
+ ## with lots of entries.
d = threads.deferToThread(self.fs_access.ls, self._path(path))
d.addCallback(success)
d.addErrback(failure)
@@ -112,8 +140,14 @@
ret.append(self._gotlisting(result, keys))
return ret
+ def goterror(failure):
+ if failure.type is NotFound:
+ raise ftp.FileNotFoundError(self._path(path))
+ raise ftp.PermissionDeniedError(self._path(path))
+
d = threads.deferToThread(self.fs_access.ls, self._path(path))
d.addCallback(gotresults, keys)
+ d.addErrback(goterror)
return d
@@ -167,28 +201,34 @@
def send(self, path, consumer):
def finished(result, consumer):
consumer.transport.loseConnection()
- def failed(result, path):
+
+ def failed(failure, consumer):
consumer.transport.loseConnection()
- ## XXX - a more appropriate exception here.
- raise ftp.PermissionDeniedError(path)
+ if failure.type is NotFound:
+ raise ftp.FileNotFoundError(self._path(path))
+ ## Unauthorized error - is there any other errors I should
+ ## be catching.
+ raise ftp.PermissionDeniedError(self._path(path))
p = self._path(path)
d = threads.deferToThread(self.fs_access.readfile, p, consumer)
d.addCallback(finished, consumer)
- d.addErrback(failed, consumer, p)
+ d.addErrback(failed, consumer)
return d
def receive(self, path):
- def accessok(result, fs, path):
+ def accessok(result, fs):
if not result:
- raise ftp.PermissionDeniedError(path)
+ raise ftp.PermissionDeniedError(self._path(path))
return ConsumerObject(fs, p)
- def failure(result, path):
+
+ def failure(result):
## XXX - should be a better exception
raise ftp.PermissionDeniedError(path)
+
p = self._path(path)
d = threads.deferToThread(self.fs_access.writable, p)
- d.addCallback(accessok, self.fs_access, p)
- d.addErrback(failure, p)
+ d.addCallback(accessok, self.fs_access)
+ d.addErrback(failure)
return d
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/test/test_zope_ftp.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/test/test_zope_ftp.py 2005-10-07 21:13:08 UTC (rev 38909)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/test/test_zope_ftp.py 2005-10-07 22:10:56 UTC (rev 38910)
@@ -73,7 +73,7 @@
root = demofs.Directory()
# the tuple has a user name is used by ZopeSimpleAuthentication to
# authenticate users.
- root.grant(('root', 'root'), demofs.write)
+ root.grant('root', demofs.write)
self.rootfs = rootfs = DemoFileSystem(root, ('root', 'root'))
# Start the server
@@ -110,8 +110,26 @@
)
class BasicFTPServerTestCase(FTPServerTestCase, test_ftp.BasicFTPServerTestCase):
- pass
+ def _authLogin(self):
+ responseLines = wait(self.client.queueStringCommand('USER root'))
+ self.assertEquals(
+ ['331 Password required for root.'],
+ responseLines
+ )
+ responseLines = wait(self.client.queueStringCommand(
+ 'PASS root')
+ )
+ self.assertEquals(
+ ['230 User logged in, proceed'],
+ responseLines
+ )
+
+ def test_MKD(self):
+ self._authLogin()
+ responseLines = wait(self.client.queueStringCommand('MKD /newdir'))
+ self.assertEqual(['257 "/newdir" created'], responseLines)
+
class FTPServerPasvDataConnectionTestCase(FTPServerTestCase,
test_ftp.FTPServerPasvDataConnectionTestCase):
@@ -130,8 +148,6 @@
# Make some directories
self.rootfs.mkdir_nocheck('/foo')
self.rootfs.mkdir_nocheck('/bar')
-## os.mkdir(os.path.join(self.directory, 'foo'))
-## os.mkdir(os.path.join(self.directory, 'bar'))
# Download a listing again
downloader = self._makeDataConnection()
@@ -174,9 +190,6 @@
# Download a range of different size files
for size in range(100000, 110000, 500):
self.rootfs.writefile_nocheck('/%d.txt' % (size,), StringIO('x' * size))
-## fObj = file(os.path.join(self.directory, '%d.txt' % (size,)), 'wb')
-## fObj.write('x' * size)
-## fObj.close()
downloader = self._makeDataConnection()
d = self.client.queueStringCommand('RETR %d.txt' % (size,))
@@ -193,3 +206,67 @@
l = [defer.maybeDeferred(port.stopListening) for port in self.dataPorts]
wait(defer.DeferredList(l, fireOnOneErrback=True))
return FTPServerPasvDataConnectionTestCase.tearDown(self)
+
+from twisted.test.test_ftp import _BufferingProtocol
+
+class ZopeFTPPermissionTestCases(FTPServerTestCase):
+ def setUp(self):
+ FTPServerTestCase.setUp(self)
+ self.filename = 'nopermissionfolder'
+ self.rootfs.writefile('/%s' % self.filename, StringIO('x' * 100))
+ file = self.rootfs.get(self.filename)
+ file.grant('michael', 0)
+ del file.access['anonymous']
+
+ def _makeDataConnection(self):
+ # Establish a passive data connection (i.e. client connecting to
+ # server).
+ responseLines = wait(self.client.queueStringCommand('PASV'))
+ host, port = ftp.decodeHostPort(responseLines[-1][4:])
+ downloader = wait(
+ protocol.ClientCreator(reactor,
+ _BufferingProtocol).connectTCP('127.0.0.1',
+ port)
+ )
+ return downloader
+
+ def _michaelLogin(self):
+ responseLines = wait(self.client.queueStringCommand('USER michael'))
+ self.assertEquals(
+ ['331 Password required for michael.'],
+ responseLines
+ )
+
+ responseLines = wait(self.client.queueStringCommand(
+ 'PASS michael')
+ )
+ self.assertEquals(
+ ['230 User logged in, proceed'],
+ responseLines
+ )
+
+ def testNoSuchDirectory(self):
+ self._michaelLogin()
+ deferred = self.client.queueStringCommand('CWD /nosuchdir')
+ failureResponseLines = self._waitForCommandFailure(deferred)
+ self.failUnless(failureResponseLines[-1].startswith('550'),
+ "Response didn't start with 550: %r" % failureResponseLines[-1])
+
+ def testListNonPermission(self):
+ self._michaelLogin()
+
+ # Download a listing
+ downloader = self._makeDataConnection()
+ d = self.client.queueStringCommand('NLST ')
+ wait(defer.gatherResults([d, downloader.d]))
+
+ # No files, so the file listing should be empty
+ filenames = downloader.buffer[:-2].split('\r\n')
+ filenames.sort()
+ self.assertEqual([self.filename], filenames)
+
+ downloader = self._makeDataConnection()
+ d = self.client.queueStringCommand('RETR %s' % self.filename)
+ failureResponseLines = self._waitForCommandFailure(d)
+ self.failUnless(failureResponseLines[-1].startswith('426'),
+ "Response didn't start with 426 i.e. data connection closed error")
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/demofs.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/demofs.py 2005-10-07 21:13:08 UTC (rev 38909)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/demofs.py 2005-10-07 22:10:56 UTC (rev 38910)
@@ -15,8 +15,9 @@
"""
import posixpath
from zope.security.interfaces import Unauthorized
+from zope.publisher.interfaces import NotFound
+
from zope.app.twisted.interfaces import IFileSystem
-## from zope.server.interfaces.ftp import IFileSystemAccess
from zope.interface import implements
execute = 1
@@ -31,6 +32,8 @@
self.access = {'anonymous': read}
def accessable(self, user, access=read):
+ if isinstance(user, tuple) and len(user) == 2:
+ user = user[0]
return (user == 'root'
or (self.access.get(user, 0) & access)
or (self.access.get('anonymous', 0) & access)
@@ -76,7 +79,8 @@
File = File
Directory = Directory
- def __init__(self, files, user=''):
+ def __init__(self, files, user = ''):
+ ## user is not username, password
self.files = files
self.user = user
@@ -101,7 +105,7 @@
def getany(self, path):
d = self.get(path)
if d is None:
- raise OSError("No such file or directory:", path)
+ raise NotFound(self, path)
return d
def getdir(self, path):
@@ -165,6 +169,9 @@
"See zope.server.interfaces.ftp.IFileSystem"
f = self.getfile(path)
+ if not f.accessable(self.user, read):
+ raise Unauthorized(path)
+
data = f.data
if end is not None:
data = data[:end]
@@ -195,7 +202,7 @@
if name in d.files:
raise OSError("Already exists:", name)
newdir = self.Directory()
- newdir.grant(self.user, read | write)
+ newdir.grant(self.user[0], read | write)
d.files[name] = newdir
def remove(self, path):
@@ -244,7 +251,7 @@
if f is None:
d = self.getwdir(path)
f = d.files[name] = self.File()
- f.grant(self.user, read | write)
+ f.grant(self.user[0], read | write)
elif f.type != 'f':
raise OSError("Can't overwrite a directory")
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_demofs.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_demofs.py 2005-10-07 21:13:08 UTC (rev 38909)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_demofs.py 2005-10-07 22:10:56 UTC (rev 38910)
@@ -25,7 +25,7 @@
def setUp(self):
root = demofs.Directory()
root.grant('bob', demofs.write)
- fs = self.filesystem = demofs.DemoFileSystem(root, 'bob')
+ fs = self.filesystem = demofs.DemoFileSystem(root, ('bob', '123'))
fs.mkdir(self.dir_name)
fs.writefile(self.file_name, StringIO(self.file_contents))
fs.writefile(self.unwritable_filename, StringIO("save this"))
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_publisher.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_publisher.py 2005-10-07 21:13:08 UTC (rev 38909)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/twisted/ftp/tests/test_publisher.py 2005-10-07 22:10:56 UTC (rev 38910)
@@ -106,13 +106,13 @@
def setUp(self):
root = demofs.Directory()
root.grant('bob', demofs.write)
- fs = DemoFileSystem(root, 'bob')
+ fs = DemoFileSystem(root, ('bob', '123'))
fs.mkdir(self.dir_name)
fs.writefile(self.file_name, StringIO(self.file_contents))
fs.writefile(self.unwritable_filename, StringIO("save this"))
fs.get(self.unwritable_filename).revoke('bob', demofs.write)
- self.filesystem = PublisherFileSystem('bob', RequestFactory(fs))
+ self.filesystem = PublisherFileSystem(('bob', '123'), RequestFactory(fs))
def test_suite():
return TestSuite((
More information about the Zope3-Checkins
mailing list