[Zope-Checkins] CVS: Zope3/lib/python/Zope/Server/FTP - TestFilesystemAccess.py:1.1.2.1 UsernamePassword.py:1.1.2.1 FTPServer.py:1.1.2.11 FTPServerChannel.py:1.1.2.21 PublisherFTPServer.py:1.1.2.6 RecvChannel.py:1.1.2.7 XmitChannel.py:1.1.2.6

Shane Hathaway shane@cvs.zope.org
Wed, 10 Apr 2002 17:59:52 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/Server/FTP
In directory cvs.zope.org:/tmp/cvs-serv18340/FTP

Modified Files:
      Tag: Zope3-Server-Branch
	FTPServer.py FTPServerChannel.py PublisherFTPServer.py 
	RecvChannel.py XmitChannel.py 
Added Files:
      Tag: Zope3-Server-Branch
	TestFilesystemAccess.py UsernamePassword.py 
Log Message:
- Removed AlternateSocketMapMixin.  It was there to support multiple socket
maps, but if we really need multiple socket maps, it would be better to
change asyncore.  Had to update a lot of __init__ methods.

- Added IUsernamePassword and IFilesystemAccess, which provide a way to
implement all kinds of authentication schemes without FTP knowing about
any of the details.  Modified FTPServerChannel to work based on an
IFilesystemAccess object.

- Added detection of the ZOPE_SERVER_DEBUG env var.

- Added comments here and there.

- Fixed CRs :-)


=== Added File Zope3/lib/python/Zope/Server/FTP/TestFilesystemAccess.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Implementation of IFilesystemAccess intended only for testing.

$Id: TestFilesystemAccess.py,v 1.1.2.1 2002/04/10 21:59:21 shane Exp $
"""

from Zope.Server.VFS.IFilesystemAccess import IFilesystemAccess
from Zope.Server.VFS.IUsernamePassword import IUsernamePassword
from Zope.Exceptions import Unauthorized


class TestFilesystemAccess:

    __implements__ = IFilesystemAccess

    passwords = {'foo': 'bar'}

    def __init__(self, fs):
        self.fs = fs

    def authenticate(self, credentials):
        if not IUsernamePassword.isImplementedBy(credentials):
            raise Unauthorized
        name = credentials.getUserName()
        if not self.passwords.has_key(name):
            raise Unauthorized
        if credentials.getPassword() != self.passwords[name]:
            raise Unauthorized

    def open(self, credentials):
        self.authenticate(credentials)
        return self.fs




=== Added File Zope3/lib/python/Zope/Server/FTP/UsernamePassword.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""

$Id: UsernamePassword.py,v 1.1.2.1 2002/04/10 21:59:21 shane Exp $
"""

from Zope.Server.VFS.IUsernamePassword import IUsernamePassword


class UsernamePassword:

    __implements__ = IUsernamePassword

    def __init__(self, username, password):
        self.username = username
        self.password = password

    def getUserName(self):
        return self.username

    def getPassword(self):
        return self.password



=== Zope3/lib/python/Zope/Server/FTP/FTPServer.py 1.1.2.10 => 1.1.2.11 ===
 from FTPServerChannel import FTPServerChannel
 from Zope.Server.ServerBase import ServerBase
+from Zope.Server.VFS.IFilesystemAccess import IFilesystemAccess
 
-from Zope.Server.VFS.UnixFileSystem import \
-     SchizophrenicUnixFileSystem
-from Zope.Server.Authentication.DictionaryAuthentication import \
-     DictionaryAuthentication
-
-
-class FileSystemOpener:
-
-    filesystem_class = SchizophrenicUnixFileSystem
-
-    def __init__(self, root_dir):
-        self.root_dir = root_dir
-
-    def __call__(self, username):
-        persona = pwd.getpwnam(username)[2:4]
-        return self.filesystem_class(self.root_dir, persona)
 
 
 class FTPServer(ServerBase):
@@ -45,24 +30,23 @@
     SERVER_IDENT = 'Zope.Server.FTPServer'
 
 
-    def __init__(self, ip, port, dir='/', auth_source=None,
-                 task_dispatcher=None, adj=None, start=1, hit_log=None,
-                 verbose=0, socket_map=None):
-
-        self.openFilesystem = FileSystemOpener(dir)
-        self.auth_source = auth_source
-
-        super(FTPServer, self).__init__(ip, port, task_dispatcher,
-                                        adj, start, hit_log,
-                                        verbose, socket_map)
+    def __init__(self, ip, port, fs_access, *args, **kw):
+
+        assert IFilesystemAccess.isImplementedBy(fs_access)
+        self.fs_access = fs_access
+
+        super(FTPServer, self).__init__(ip, port, *args, **kw)
 
 
 if __name__ == '__main__':
     from Zope.Server.TaskThreads import ThreadedTaskDispatcher
+    from Zope.Server.VFS.OSFileSystem import OSFileSystem
+    from TestFilesystemAccess import TestFilesystemAccess
     td = ThreadedTaskDispatcher()
     td.setThreadCount(4)
-    auth_source = DictionaryAuthentication({'root': 'bar'})
-    FTPServer('', 8021, '/', auth_source, task_dispatcher=td)
+    fs = OSFileSystem('/')
+    fs_access = TestFilesystemAccess(fs)
+    FTPServer('', 8021, fs_access, task_dispatcher=td)
     try:
         while 1:
             asyncore.poll(5)


=== Zope3/lib/python/Zope/Server/FTP/FTPServerChannel.py 1.1.2.20 => 1.1.2.21 ===
 from RecvChannel import RecvChannel
 from XmitChannel import XmitChannel, ApplicationXmitStream
+from UsernamePassword import UsernamePassword
+from Zope.Exceptions import Unauthorized
 
 
 class FTPServerChannel(LineServerChannel):
@@ -70,9 +72,8 @@
     type_mode_map = {'a':'t', 'i':'b', 'e':'b', 'l':'b'}
 
 
-    def __init__(self, server, conn, addr, adj=None, socket_map=None):
-        super(FTPServerChannel, self).__init__(server, conn, addr,
-                                               adj, socket_map)
+    def __init__(self, server, conn, addr, adj=None):
+        super(FTPServerChannel, self).__init__(server, conn, addr, adj)
 
         self.client_addr = (addr[0], 21)
 
@@ -85,14 +86,14 @@
         self._rnfr = None
 
         self.username = ''
-        self.password = ''
+        self.credentials = None
 
         self.reply('SERVER_READY', self.server.server_name)
 
 
     def _getFilesystem(self):
-        """Open the filesystem using the username and password."""
-        return self.server.openFilesystem(self.username, self.password)
+        """Open the filesystem using the current credentials."""
+        return self.server.fs_access.open(self.credentials)
         
 
     ############################################################
@@ -222,13 +223,18 @@
 
     def cmd_pass(self, args):
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
-        self.password = args
-        self.authenticated, message = self.authenticate()
-        if self.authenticated:
-            self.reply('LOGIN_SUCCESS')
-        else:
+        self.authenticated = 0
+        password = args
+        credentials = UsernamePassword(self.username, password)
+        try:
+            self.server.fs_access.authenticate(credentials)
+        except Unauthorized:
             self.reply('LOGIN_MISMATCH')
             self.close_when_done()
+        else:
+            self.credentials = credentials
+            self.authenticated = 1
+            self.reply('LOGIN_SUCCESS')
 
 
     def cmd_pasv(self, args):
@@ -439,6 +445,7 @@
 
     def cmd_user(self, args):
         'See Zope.Server.FTP.IFTPCommandHandler.IFTPCommandHandler'
+        self.authenticated = 0
         if len(args) > 1:
             self.username = args
             self.reply('PASS_REQUIRED')
@@ -454,17 +461,13 @@
             path = args
         else:
             path = os.path.join(self.cwd, args)
-        path = os.path.normpath(path)
+        # Note: don't use os.path.normpath() here!  Otherwise Zope won't
+        # work on case-insensitive platforms.
         return path
 
 
-    def authenticate(self):
-        auth = self.server.auth_source
-        return auth.authenticate(self.username, self.password)
-        
-
     def newPassiveAcceptor(self):
-            # ensure that only one of these exists at a time.
+        # ensure that only one of these exists at a time.
         if self.passive_acceptor is not None:
             self.passive_acceptor.close()
             self.passive_acceptor = None


=== Zope3/lib/python/Zope/Server/FTP/PublisherFTPServer.py 1.1.2.5 => 1.1.2.6 ===
     def __init__(self, request_factory, name, ip, port,
                  task_dispatcher=None, adj=None, start=1, hit_log=None,
-                 verbose=0, socket_map=None):
+                 verbose=0):
 
         self.request_factory = request_factory
         self.openFilesystem = PublisherFileSystemOpener('/', request_factory)
 
         super(PublisherFTPServer, self).__init__(ip, port, task_dispatcher,
                                                  adj, start, hit_log,
-                                                 verbose, socket_map)
+                                                 verbose)


=== Zope3/lib/python/Zope/Server/FTP/RecvChannel.py 1.1.2.6 => 1.1.2.7 ===
     _fileno = None  # provide a default for asyncore.dispatcher._fileno
 
-    def __init__ (self, control_channel, finish_args,
-                  adj=None, socket_map=None):
+    def __init__ (self, control_channel, finish_args):
         self.control_channel = control_channel
         self.finish_args = finish_args
         self.inbuf = OverflowableBuffer(control_channel.adj.inbuf_overflow)
-        ChannelBaseClass.__init__(self, None, None, adj, socket_map)
+        ChannelBaseClass.__init__(self, None, None, control_channel.adj)
         # Note that this channel starts out in async mode.
 
     def writable (self):


=== Zope3/lib/python/Zope/Server/FTP/XmitChannel.py 1.1.2.5 => 1.1.2.6 ===
     _fileno = None  # provide a default for asyncore.dispatcher._fileno
 
-    def __init__ (self, control_channel, ok_reply_args,
-                  adj=None, socket_map=None):
+    def __init__ (self, control_channel, ok_reply_args):
         self.control_channel = control_channel
         self.ok_reply_args = ok_reply_args
         self.set_sync()
-        ChannelBaseClass.__init__(self, None, None, adj, socket_map)
+        ChannelBaseClass.__init__(self, None, None, control_channel.adj)
 
     def _open(self):
         """Signal the client to open the connection."""