[Zope-Checkins] CVS: ZODB3/ZEO - zpasswd.py:1.1.2.1 Exceptions.py:1.6.12.1 ServerStub.py:1.12.12.1 StorageServer.py:1.92.10.1 runzeo.py:1.12.10.1

Johan Dahlin jdahlin@telia.com
Tue, 29 Apr 2003 16:03:25 -0400


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

Modified Files:
      Tag: ZODB3-auth-branch
	Exceptions.py ServerStub.py StorageServer.py runzeo.py 
Added Files:
      Tag: ZODB3-auth-branch
	zpasswd.py 
Log Message:
Initial checkin of AuthZEO (without SRP)


=== Added File ZODB3/ZEO/zpasswd.py ===
##############################################################################
#
# Copyright (c) 2003 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
#
##############################################################################
"""Usage:
        zpasswd [-cd] passwordfile username
        zpasswd -b[cd] passwordfile username password

        zpasswd -n[d] username
        zpasswd -nb[d] username password
 -c  Create a new file.
 -d  Delete user
 -n  Don't update file; display results on stdout.
 -b  Use the password from the command line rather than prompting for it."""

import sys
import getopt
import getpass

from ZEO.auth.database import Database
#from ZEO.auth.srp import SRPDatabase
   
try:
    opts, args = getopt.getopt(sys.argv[1:], 'cdnbs')
except getopt.GetoptError:
    # print help information and exit:
    print __doc__
    sys.exit(2)

stdout = 0
create = 0
delete = 0
prompt = 1
#srp = 0

for opt, arg in opts:
    if opt in ("-h", "--help"):
        print __doc__
        sys.exit()
    if opt == "-n":
        stdout = 1
    if opt == "-c":
        create = 1
    if opt == "-d":
        delete = 1
    if opt == "b":
        prompt = 0
#    if opt == "-s":
#        srp = 1

if create and delete:
    print "Can't create and delete at the same time"
    sys.exit(3)

if len(args) < 2:
    print __doc__
    sys.exit()

output = args[0]
username = args[1]

if not delete:
    if len(args) > 3:
        print __doc__
        sys.exit()
        
    if prompt:
        password = getpass.getpass('Enter passphrase: ')
    else:
        password = args[2]

#if srp:
#    db = SRPDatabase(output)
#else:
db = Database(output)

if create:
    try:
        db.add_user(username, password)
    except LookupError:
        print 'The username already exists'
        sys.exit(4)
    if stdout:
        db.save(fd=sys.stdout)
    else:
        db.save()
    
if delete:
    try:
        db.del_user(username)
    except LockupError:
        print 'The username doesn\'t exist'
        sys.exit(5)
    if stdout:
        db.save(fd=sys.stdout)
    else:
        db.save()



=== ZODB3/ZEO/Exceptions.py 1.6 => 1.6.12.1 ===
--- ZODB3/ZEO/Exceptions.py:1.6	Wed Jan 15 13:19:15 2003
+++ ZODB3/ZEO/Exceptions.py	Tue Apr 29 16:02:54 2003
@@ -24,3 +24,5 @@
 class ClientDisconnected(ClientStorageError):
     """The database storage is disconnected from the storage."""
 
+class AuthError(StorageError):
+    """The client provided invalid authentication credentials."""


=== ZODB3/ZEO/ServerStub.py 1.12 => 1.12.12.1 ===
--- ZODB3/ZEO/ServerStub.py:1.12	Tue Jan 14 14:08:33 2003
+++ ZODB3/ZEO/ServerStub.py	Tue Apr 29 16:02:54 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,
@@ -45,6 +45,9 @@
     def get_info(self):
         return self.rpc.call('get_info')
 
+    def getAuthProtocol(self):
+        return self.rpc.call('getAuthProtocol')
+    
     def lastTransaction(self):
         # Not in protocol version 2.0.0; see __init__()
         return self.rpc.call('lastTransaction')


=== ZODB3/ZEO/StorageServer.py 1.92 => 1.92.10.1 ===
--- ZODB3/ZEO/StorageServer.py:1.92	Mon Jan 20 16:26:31 2003
+++ ZODB3/ZEO/StorageServer.py	Tue Apr 29 16:02:54 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,
@@ -31,6 +31,7 @@
 
 from ZEO import ClientStub
 from ZEO.CommitLog import CommitLog
+from ZEO.auth.database import Database
 from ZEO.monitor import StorageStats, StatsServer
 from ZEO.zrpc.server import Dispatcher
 from ZEO.zrpc.connection import ManagedServerConnection, Delay, MTDelay
@@ -161,6 +162,8 @@
         """Select the storage that this client will use
 
         This method must be the first one called by the client.
+        For authenticated storages this method will be called by the client
+        immediately after authentication is finished.
         """
         if self.storage is not None:
             self.log("duplicate register() call")
@@ -410,6 +413,15 @@
         else:
             return self._wait(lambda: self._vote())
 
+    def getAuthProtocol(self):
+        """Return string specifying name of authentication module to use.
+
+           The module name should be auth_%s where %s is auth_protocol."""
+        protocol = self.server.auth_protocol
+        if not protocol or protocol == 'none':
+            return None
+        return protocol
+    
     def abortVersion(self, src, id):
         self._check_tid(id, exc=StorageTransactionError)
         if self.locked:
@@ -577,7 +589,9 @@
     def __init__(self, addr, storages, read_only=0,
                  invalidation_queue_size=100,
                  transaction_timeout=None,
-                 monitor_address=None):
+                 monitor_address=None,
+                 auth_protocol=None,
+                 auth_filename=None):
         """StorageServer constructor.
 
         This is typically invoked from the start.py script.
@@ -618,7 +632,22 @@
         monitor_address -- The address at which the monitor server
             should listen.  If specified, a monitor server is started.
             The monitor server provides server statistics in a simple
-            text format. 
+            text format.
+
+        auth_protocol -- The name of the authentication protocol to use.
+            Examples are "plaintext", "sha" and "srp".
+            
+        auth_filename -- The name of the password database filename.
+            It should be in a format compatible with the authentication
+            protocol used; for instance, "sha" and "srp" require different
+            formats.
+            
+            Note that to implement an authentication protocol, a server
+            and client authentication mechanism must be implemented in a
+            auth_* module, which should be stored inside the "auth"
+            subdirectory. This module may also define a DatabaseClass
+            variable that should indicate what database should be used
+            by the authenticator.
         """
 
         self.addr = addr
@@ -633,6 +662,10 @@
         for s in storages.values():
             s._waiting = []
         self.read_only = read_only
+        self.auth_protocol = auth_protocol
+        self.auth_filename = auth_filename
+        if auth_protocol:
+            self._setup_auth(auth_protocol)
         # A list of at most invalidation_queue_size invalidations
         self.invq = []
         self.invq_bound = invalidation_queue_size
@@ -654,7 +687,43 @@
             self.monitor = StatsServer(monitor_address, self.stats)
         else:
             self.monitor = None
+            
+    def _setup_auth(self, protocol):
+        # Load the auth protocol
+        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))
+            self.auth_protocol = None
+            return
+        
+        from ZEO.auth.storage import AuthZEOStorage
+        
+        # And set up ZEOStorageClass
+        klass = getattr(module, 'StorageClass', None)
+        if not klass or not issubclass(klass, AuthZEOStorage):
+            log(("%s: %s is not a valid auth protocol, must have a " + \
+                "StorageClass class") % (self.__class__.__name__, protocol))
+            self.auth_protocol = None
+            return
+        self.ZEOStorageClass = klass
 
+        log("%s: using auth protocol: %s" % \
+            (self.__class__.__name__, protocol))
+        
+        dbklass = getattr(module, 'DatabaseClass', None)
+        if not dbklass:
+            dbklass = Database
+
+        # We create a Database instance here for use with the authenticator
+        # modules. Having one instance allows it to be shared between multiple
+        # storages, avoiding the need to bloat each with a new authenticator
+        # Database that would contain the same info, and also avoiding any
+        # possibly synchronization issues between them.
+        self.database = dbklass(self.auth_filename)
+        
     def new_connection(self, sock, addr):
         """Internal: factory to create a new connection.
 
@@ -663,6 +732,8 @@
         connection.
         """
         z = self.ZEOStorageClass(self, self.read_only)
+        if self.auth_protocol:
+            z.set_database(self.database)
         c = self.ManagedServerConnectionClass(sock, addr, z, self)
         log("new connection %s: %s" % (addr, `c`))
         return c


=== ZODB3/ZEO/runzeo.py 1.12 => 1.12.10.1 ===
--- ZODB3/ZEO/runzeo.py:1.12	Fri Jan 24 17:37:20 2003
+++ ZODB3/ZEO/runzeo.py	Tue Apr 29 16:02:54 2003
@@ -1,7 +1,7 @@
 #!python
 ##############################################################################
 #
-# 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,
@@ -89,7 +89,9 @@
                  "t:", "timeout=", float)
         self.add("monitor_address", "zeo.monitor_address", "m:", "monitor=",
                  self.handle_monitor_address)
-
+        self.add('auth_protocol', 'zeo.auth_protocol', None,
+                 'auth-protocol=', default=None)
+        self.add('auth_filename', 'zeo.auth_filename', None, 'auth-filename=')
 
 class ZEOOptions(ZDOptions, ZEOOptionsMixin):
 
@@ -189,7 +191,9 @@
             read_only=self.options.read_only,
             invalidation_queue_size=self.options.invalidation_queue_size,
             transaction_timeout=self.options.transaction_timeout,
-            monitor_address=self.options.monitor_address)
+            monitor_address=self.options.monitor_address,
+            auth_protocol=self.options.auth_protocol,
+            auth_filename=self.options.auth_filename)
 
     def loop_forever(self):
         import ThreadedAsync.LoopCallback