[Zodb-checkins] CVS: ZODB3/ZEO - ClientStorage.py:1.93.2.1

Jeremy Hylton jeremy at zope.com
Tue May 13 17:42:39 EDT 2003


Update of /cvs-repository/ZODB3/ZEO
In directory cvs.zope.org:/tmp/cvs-serv10167/ZEO

Modified Files:
      Tag: ZODB3-auth-branch
	ClientStorage.py 
Log Message:
Add ClientStorage support for authentication.

XXX Somehow this was lost when the rest of the branch was checked in.


=== ZODB3/ZEO/ClientStorage.py 1.93 => 1.93.2.1 ===
--- ZODB3/ZEO/ClientStorage.py:1.93	Tue Apr 22 14:00:16 2003
+++ ZODB3/ZEO/ClientStorage.py	Tue May 13 16:42:38 2003
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright (c) 2001, 2002, 2003 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -29,7 +29,8 @@
 from ZEO import ClientCache, ServerStub
 from ZEO.TransactionBuffer import TransactionBuffer
 from ZEO.Exceptions \
-     import ClientStorageError, UnrecognizedResult, ClientDisconnected
+     import ClientStorageError, UnrecognizedResult, ClientDisconnected, \
+            AuthError
 from ZEO.zrpc.client import ConnectionManager
 
 from ZODB import POSException
@@ -99,7 +100,8 @@
                  min_disconnect_poll=5, max_disconnect_poll=300,
                  wait_for_server_on_startup=None, # deprecated alias for wait
                  wait=None, # defaults to 1
-                 read_only=0, read_only_fallback=0):
+                 read_only=0, read_only_fallback=0,
+                 username='', password=''):
 
         """ClientStorage constructor.
 
@@ -159,6 +161,17 @@
             writable storages are available.  Defaults to false.  At
             most one of read_only and read_only_fallback should be
             true.
+
+        username -- string with username to be used when authenticating.
+            These only need to be provided if you are connecting to an
+            authenticated server storage.
+ 
+        password -- string with plaintext password to be used
+            when authenticated.
+
+        Note that the authentication protocol is defined by the server
+        and is detected by the ClientStorage upon connecting (see
+        testConnection() and doAuth() for details).
         """
 
         log2(INFO, "%s (pid=%d) created %s/%s for storage: %r" %
@@ -217,6 +230,8 @@
         self._conn_is_read_only = 0
         self._storage = storage
         self._read_only_fallback = read_only_fallback
+        self._username = username
+        self._password = password
         # _server_addr is used by sortKey()
         self._server_addr = None
         self._tfile = None
@@ -293,18 +308,21 @@
                     break
                 log2(INFO, "Wait for cache verification to finish")
         else:
-            # If there is no mainloop running, this code needs
-            # to call poll() to cause asyncore to handle events.
-            while 1:
-                if self._ready.isSet():
-                    break
-                log2(INFO, "Wait for cache verification to finish")
-                if self._connection is None:
-                    # If the connection was closed while we were
-                    # waiting for it to become ready, start over.
-                    return self._wait()
-                else:
-                    self._connection.pending(30)
+            self._wait_sync()
+
+    def _wait_sync(self):
+        # If there is no mainloop running, this code needs
+        # to call poll() to cause asyncore to handle events.
+        while 1:
+            if self._ready.isSet():
+                break
+            log2(INFO, "Wait for cache verification to finish")
+            if self._connection is None:
+                # If the connection was closed while we were
+                # waiting for it to become ready, start over.
+                return self._wait()
+            else:
+                self._connection.pending(30)
 
     def close(self):
         """Storage API: finalize the storage, releasing external resources."""
@@ -344,6 +362,38 @@
         if cn is not None:
             cn.pending()
 
+    def doAuth(self, protocol, stub):
+        if self._username == '' and self._password == '':
+            raise AuthError, "empty username or password"
+
+        # import the auth module
+        # XXX: Should we validate the client module that is being specified
+        # by the server? A malicious server could cause any auth_*.py file
+        # to be loaded according to Python import semantics.
+
+        # XXX There should probably be a registry of valid authentication
+        # mechanisms for the client, and we should only import those
+        # modules.
+        
+        fullname = 'ZEO.auth.auth_' + protocol
+        try:
+            module = __import__(fullname, globals(), locals(), protocol)
+        except ImportError:
+            log("%s: no such an auth protocol: %s" %
+                (self.__class__.__name__, protocol))
+
+        # instantiate the client authenticator
+        Client = getattr(module, 'Client', None)
+        if not Client:
+            log("%s: %s is not a valid auth protocol, must have a " + \
+                "Client class" % (self.__class__.__name__, protocol))
+            raise AuthError, "invalid protocol"
+        
+        c = Client(stub)
+        
+        # Initiate authentication, returns boolean specifying whether OK
+        return c.start(self._username, self._password)
+        
     def testConnection(self, conn):
         """Internal: test the given connection.
 
@@ -369,6 +419,12 @@
         # XXX Check the protocol version here?
         self._conn_is_read_only = 0
         stub = self.StorageServerStubClass(conn)
+
+        # XXX: Verify return value?
+        auth = stub.getAuthProtocol()
+        if auth and not self.doAuth(auth, stub):
+            raise AuthError, "Authentication failed"
+        
         try:
             stub.register(str(self._storage), self._is_read_only)
             return 1
@@ -414,6 +470,9 @@
         self._oids = []
         self._info.update(stub.get_info())
         self.verify_cache(stub)
+        if not conn.is_async():
+            log2(INFO, "Waiting for cache verification to finish")
+            self._wait_sync()
 
     def set_server_addr(self, addr):
         # Normalize server address and convert to string




More information about the Zodb-checkins mailing list