[Zope-Checkins] CVS: ZODB3/ZEO/auth - .cvsignore:1.1.2.1 __init__.py:1.1.2.1 auth_plaintext.py:1.1.2.1 auth_sha.py:1.1.2.1 database.py:1.1.2.1 hmac.py:1.1.2.1 storage.py:1.1.2.1

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


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

Added Files:
      Tag: ZODB3-auth-branch
	.cvsignore __init__.py auth_plaintext.py auth_sha.py 
	database.py hmac.py storage.py 
Log Message:
Initial checkin of AuthZEO (without SRP)


=== Added File ZODB3/ZEO/auth/.cvsignore ===
*.pyo
*.pyc


=== Added File ZODB3/ZEO/auth/__init__.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
#
##############################################################################


=== Added File ZODB3/ZEO/auth/auth_plaintext.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
#
##############################################################################
"""Implements plaintext password authentication. The password is stored in
an SHA hash in the Database. The client sends over the plaintext
password, and the SHA hashing is done on the server side. 
 
This mechanism offers *no network security at all*; the only security
is provided by not storing plaintext passwords on disk.  (See the
auth_srp module for a secure mechanism)"""

import sha
from ZEO.auth.storage import AuthZEOStorage

class StorageClass(AuthZEOStorage):
    def auth(self, username, password):
        try:
            dbpw = self.database.get_password(username)
        except LookupError:
            return 0
        password = sha.new(password).hexdigest()
        return self.finish_auth(dbpw == password)
    
class Client:
    def __init__(self, stub):
        self.stub = stub

    def start(self, username, password):
        method = self.stub.extensionMethod('auth')
        return method(username, password)


=== Added File ZODB3/ZEO/auth/auth_sha.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
#
##############################################################################
"""Implements simple hashed password authentication. The password is stored in
an SHA hash in the Database. The client sends over the hashed password,
which is verified by the server.
 
This mechanism offers *very weak network security*; the password
hash is capturable and the mechanism is vulnerable to trivial replay
attacks. (See the auth_srp module for a secure mechanism)"""

import sha

from ZEO.auth.storage import AuthZEOStorage

class StorageClass(AuthZEOStorage):
    def auth(self, username, password):
        try:
            dbpw = self.database.get_password(username)
        except LookupError:
            return 0
        return self.finish_auth(dbpw == password)

class Client:
    def __init__(self, stub):
        self.stub = stub

    def start(self, username, password):
        method = self.stub.extensionMethod('auth')
        return method(username, sha.new(password).hexdigest())


=== Added File ZODB3/ZEO/auth/database.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
#
##############################################################################

import os
import sha
from types import StringType

class Database:
    """Abstracts a password database. This class is used both in the
       authentication process (via get_password()) and by client scripts
       that manage the password database file.

       The password file is a simple, colon-separated text file mapping
       usernames to password hashes. The hashes are SHA hex digests
       produced from the password string."""
    def __init__(self, filename):
        """Creates a new Database

           filename: a string containing the full pathname of
               the password database file. Must be readable by the user
               running ZEO. Must be writeable by any client script that
               accesses the database."""
        
        self._users = {}
        self.filename = filename
        self.load()
        
    def save(self, fd=None):
        filename = self.filename

        if not fd:
            fd = open(filename, 'w')
        
        for username, hash in self._users.items():
            fd.write('%s:%s\n' % (username, hash))
            
    def load(self):
        filename = self.filename
        if not filename:
            return

        if not os.path.exists(filename):
            return
        
        fd = open(filename)
        for line in fd.readlines():
            username, hash = line[:-1].split(':', 1)
            self._users[username] = hash

    def _store_password(self, username, password):
        if type(password) <> StringType:
            raise TypeError, "password must be a string"
        self._users[username] = self.hash(password)

    def get_password(self, username):
        """Returns password hash for specified username.

        Callers must check for LookupError, which is raised in
        the case of a non-existent user specified."""
	if not self._users.has_key(username):
            raise LookupError, "No such user: %s" % username
        return self._users[username]
    
    def hash(self, s):
        return sha.new(s).hexdigest()

    def add_user(self, username, password):
        if self._users.has_key(username):
            raise LookupError, "User %s does already exist" % username
        if type(username) <> StringType:
            raise TypeError, "username must be a string"
        self._store_password(username, password)

    def del_user(self, username):
	if not self._users.has_key(username):
            raise LookupError, "No such user: %s" % username
        del self._users[username]

    def change_password(self, username, password):
        if not self._users.has_key(username):
            raise LookupError, "No such user: %s" % username
        self._store_password(username, password)


=== Added File ZODB3/ZEO/auth/hmac.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
#
##############################################################################
"""HMAC -- keyed hashing for message authentication, as described in rfc2104.

Author: Tom Holroyd <tomh@kurage.nimh.nih.gov>
Part of the SRPSocket package: 
http://members.tripod.com/professor_tom/archives/srpsocket.html
"""

import sha

BLEN = 64
ipad = map(ord, "\x36" * BLEN)
opad = map(ord, "\x5C" * BLEN)

def hash(s):
    return sha.new(s).digest()

def hmac(key, text):
    """Given strings 'key' and 'text', produce an HMAC digest."""

    # If the key is longer than BLEN, hash it first.  The result must
    # be less than BLEN bytes.  This depends on the hash function used;
    # sha1 and md5 are both OK.

    l = len(key)
    if l > BLEN:
        key = hash(key)
        l = len(key)

    # Pad the key with zeros to BLEN bytes.

    key = key + '\0' * (BLEN - l)
    key = map(ord, key)

    # Now compute the HMAC.

    l = map(lambda x, y: x ^ y, key, ipad)
    s = reduce(lambda x, y: x + chr(y), l, '')
    s = hash(s + text)
    l = map(lambda x, y: x ^ y, key, opad)
    t = reduce(lambda x, y: x + chr(y), l, '')
    s = hash(t + s)

    return s


=== Added File ZODB3/ZEO/auth/storage.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
#
##############################################################################

from ZEO.Exceptions import AuthError
from ZEO.StorageServer import ZEOStorage

class AuthZEOStorage(ZEOStorage):
    def __init_(self, *args, **kwargs):
        ZEOStorage.__init__(self, *args, **kwargs)
        self.authenticated = 0

    def set_database(self, database):
        self.database = database
        
    def finish_auth(self, authenticated):
        self.authenticated = authenticated
        return authenticated
    
    def register(self, storage_id, read_only):
        if not self.authenticated:
            raise AuthError, "Client was never authenticated with server!"
        
        ZEOStorage.register(self, storage_id, read_only)