[Zope3-checkins] CVS: zopeproducts/sqlauth - __init__.py:1.1 configure.zcml:1.1 interfaces.py:1.1

Albertas Agejevas alga@codeworks.lt
Fri, 18 Jul 2003 09:31:20 -0400


Update of /cvs-repository/zopeproducts/sqlauth
In directory cvs.zope.org:/tmp/cvs-serv24080

Added Files:
	__init__.py configure.zcml interfaces.py 
Log Message:
An SQL Principal Source for the Pluggable Authentication Service.
Initial checkin.


=== Added File zopeproducts/sqlauth/__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.
#
##############################################################################
"""SQL based principal source for the Pluggable Authentication Service.

$Id: __init__.py,v 1.1 2003/07/18 13:31:13 alga Exp $
"""
from __future__ import generators
import base64
from sha import sha

from zope.interface import implements
from zope.component import getService
from zope.context import ContextAwareDescriptors, ContextMethod
from zope.proxy import removeAllProxies
from zope.app.services.servicenames import SQLDatabaseConnections
from zope.app.interfaces.services.pluggableauth import IUserSchemafied
from zope.exceptions import NotFoundError
from persistence import Persistent
from interfaces import ISQLPrincipalSource

__metaclass__ = type

class SQLPrincipalSource(Persistent):
    """SQL based principal source for the Pluggable Authentication Service.

    The table should be defined thus:
    CREATE TABLE Users (
        id          SERIAL       PRIMARY KEY,
        login       VARCHAR(255) UNIQUE,
        title       VARCHAR(255) NOT NULL,
        description VARCHAR(255) NOT NULL,
        password    VARCHAR(255) NOT NULL
        )
    """
    ContextAwareDescriptors()
    implements(ISQLPrincipalSource)


    def __init__(self, connection=None, table="users"):
        """
        connection is a registered SQL database connection name,
        table      is the name of the table the users are stored in.
        """
        self.connection = connection
        self.table = table

    def _getConnection(self):
        if not hasattr(self, '_v_connection'):
            sqlservice = getService(self, SQLDatabaseConnections)
            self._v_connection = sqlservice.getConnection(self.connection)

            # Authentication should work for everyone, but we don't want to
            # make SQL access public.
            self._v_connection = removeAllProxies(self._v_connection)
        return self._v_connection

    def getPrincipal(self, id):
        "See zope.app.interfaces.services.pluggableauth.IPrincipalSource"
        conn = self._getConnection()
        try:
            return SQLPrincipal(conn, id, self.table)
        except (IndexError, TypeError):
            raise NotFoundError

    def getPrincipals(self, name):
        "See zope.app.interfaces.services.pluggableauth.IPrincipalSource"
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('SELECT id FROM "%s" where login LIKE \'%%%s%%\'' %
                       (self.table, name))
        result = []
        for row in cursor.fetchall():
            result.append(SQLPrincipal(conn, row[0], self.table))

        return result

    def __getitem__(self, key):
        "See zope.interface.common.mapping.IItemMapping"
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('SELECT id FROM "%s" WHERE login = \'%s\'' %
                       (self.table, key))
        id = cursor.fetchone()[0]
        return SQLPrincipal(conn, id, self.table)

    def get(self, key, default=None):
        "See zope.interface.common.mapping.IReadMapping"
        if key is None:
            return default
        try:
            conn = self._getConnection()
            cursor = conn.cursor()
            cursor.execute('SELECT id FROM "%s" WHERE login = \'%s\'' %
                           (self.table, key))
            id = cursor.fetchone()[0]
            return SQLPrincipal(conn, id, self.table)

            #return self[key]
        except (TypeError, IndexError):
            return default

    def __contains__(self, key):
        "See zope.interface.common.mapping.IReadMapping"
        if self.get(key):
            return True
        else:
            return False

    def keys(self):
        "See zope.interface.common.mapping.IEnumerableMapping"
        return tuple(self.__iter__())

    def __iter__(self):
        "See zope.interface.common.mapping.IEnumerableMapping"
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('SELECT id FROM "%s"' % self.table)
        for id, in cursor.fetchall():
            principal = SQLPrincipal(conn, id, self.table)
            yield principal.login

    def values(self):
        "See zope.interface.common.mapping.IEnumerableMapping"
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('SELECT id FROM "%s"' % self.table)
        result = []
        for id, in cursor.fetchall():
            principal = SQLPrincipal(conn, id, self.table)
            result.append(principal)
        return tuple(result)

    def items(self):
        "See zope.interface.common.mapping.IEnumerableMapping"
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('SELECT id FROM "%s"' % self.table)
        result = []
        for id, in cursor.fetchall():
            principal = SQLPrincipal(conn, id, self.table)
            result.append((principal.login, principal))
        return tuple(result)

    def __len__(self):
        "See zope.interface.common.mapping.IEnumerableMapping"
        if not self.connection:
            return 0
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('SELECT COUNT(*) FROM "%s"' % self.table)
        return cursor.fetchone()[0]

    def setObject(self, key, object):
        "See zope.app.interfaces.container.IWriteContainer"
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('INSERT INTO "%s" (login, password, title, description) '
                       'VALUES (\'%s\', \'%s\', \'%s\', \'%s\')' %
                       (self.table, object.login, object.password,
                        object.title, object.description))
        return object.login

    def __delitem__(self, key):
        "See zope.app.interfaces.container.IWriteContainer"
        conn = self._getConnection()
        cursor = conn.cursor()
        cursor.execute('DELETE FROM "%s" WHERE login = \'%s\'' %
                       (self.table, key))


    def authenticate(self, login, password):
        "See ILoginPasswordPrincipalSource"
        principal = self.get(login, None)
        if principal and principal.validate(password):
            return principal
        else:
            return None

class SQLPrincipal:
    """This is a simple implementation of IUserSchemafied which issues
    UPDATE SQL statements to the connection when the attributes are modified.
    """

    implements(IUserSchemafied)

    def __init__(self, connection, id, table="users"):
        """Arguments:
             connection      an IDBIConnection object
             id              the primary key of the user in the database
             table           database table with the columns named as
                             the attributes of IUserSchemafied
        """
        self._connection = connection
        self._table = table
        self._id = id
        cursor = self._connection.cursor()
        cursor.execute(
            'SELECT login, password, title, description from "%s" where id = %s'
            % (table, id))
        self._login, self._password, self._title, self._description = (
            cursor.fetchone())

    def validate(self, test_password):
        "See zope.app.interfaces.services.pluggableauth.IUserSchemafied"
        hash = base64.encodestring(sha(test_password).digest())[:-1]
        return self._password == hash

    def getId(self):
        "See zope.app.interfaces.security.IPrincipal"
        return self._id

    def getTitle(self):
        "See zope.app.interfaces.security.IPrincipal"
        return self._title

    def getDescription(self):
        "See zope.app.interfaces.security.IPrincipal"
        return self._description

    def _getLogin(self): return self._login
    def _getTitle(self): return self._title
    def _getPassword(self): return self._password
    def _getDescription(self): return self._description

    def _setLogin(self, login):
        self._set('login', login)

    def _setTitle(self, title):
        self._set('title', title)

    def _setDescription(self, description):
        self._set('description', description)

    def _setPassword(self, password):
        hash = base64.encodestring(sha(password).digest())[:-1]
        self._set('password', hash)

    def _set(self, attr, value):
        setattr(self, '_' + attr, value)
        self._connection.cursor().execute(
            'UPDATE "%s" SET %s = \'%s\' WHERE id = %s' % (self._table, attr,
                                                           value, self._id))
    def __cmp__(self, other):
        return cmp(self.id, other.id)

    id = property(getId)
    title = property(_getTitle, _setTitle)
    login = property(_getLogin, _setLogin)
    password = property(_getPassword, _setPassword)
    description = property(_getDescription, _setDescription)


=== Added File zopeproducts/sqlauth/configure.zcml ===
<zopeConfigure xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser">

<content class=".SQLPrincipalSource">
    <factory
        id="zopeproducts.sqlauth.SQLPrincipalSource"
        permission="zope.ManageServices"
        />
    <allow
        interface="zope.app.interfaces.container.IReadContainer"
        />
    <require
        permission="zope.ManageServices"
        interface="zope.app.interfaces.container.IWriteContainer"
        />
    <require
        permission="zope.ManageServices"
        interface="zopeproducts.sqlauth.interfaces.ISQLPrincipalSourceData"
        />
    <allow
        interface="zope.app.interfaces.services.pluggableauth.IPrincipalSource"
        />
</content>

<interface interface=".interfaces.ISQLPrincipalSource"/>

<content class=".SQLPrincipal">
    <factory
        id="zopeproducts.sqlauth.SQLPrincipal"
        permission="zope.ManageServices"
        />
    <allow
        interface="zope.app.interfaces.services.pluggableauth.IUserSchemafied"
        />
    <require
        permission="zope.ManageServices"
        set_schema="zope.app.interfaces.services.pluggableauth.IUserSchemafied"
        />
</content>

<browser:menuItem
    menu="add_principal_source"
    for="zope.app.interfaces.container.IAdding"
    action="AddSQLPrincipalSourceForm"
    title="SQL Principal Source"
    description="SQL Principal Source"
    />

<browser:addform
    schema="zopeproducts.sqlauth.interfaces.ISQLPrincipalSource"
    label="Add SQL Principal source"
    content_factory="zopeproducts.sqlauth.SQLPrincipalSource"
    arguments="connection table"
    fields="connection table"
    name="AddSQLPrincipalSourceForm"
    permission="zope.ManageServices" />

<browser:editform
     name="edit.html"
     menu="zmi_views" title="Edit"
     label="Edit SQL data"
     fields="connection table"
     for="zopeproducts.sqlauth.interfaces.ISQLPrincipalSource"
     schema="zopeproducts.sqlauth.interfaces.ISQLPrincipalSource"
     permission="zope.ManageServices"
     />

</zopeConfigure>

=== Added File zopeproducts/sqlauth/interfaces.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.
#
##############################################################################
"""Interfaces for the SQLPrincipalSource

$Id: interfaces.py,v 1.1 2003/07/18 13:31:13 alga Exp $
"""
import zope.schema
from zope.interface import Interface
from zope.app.interfaces.services.pluggableauth \
     import IContainerPrincipalSource, ILoginPasswordPrincipalSource
from zope.app.interfaces.container import IContainerNamesContainer
from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.interfaces.content.sql import SQLConnectionName


class ISQLPrincipalSourceData(Interface):

    connection = SQLConnectionName(
        title=_("Connection"),
        description=_("The SQL connection used"),
        )

    table = zope.schema.BytesLine(
        title=_("Table"),
        description=_(
        """The database table in which the users are stored.  This
        table has to have the columns "userid", "login" and
        "password".
        """
        ),
        )

class ISQLPrincipalSource(ISQLPrincipalSourceData, IContainerPrincipalSource,
                          IContainerNamesContainer, ILoginPasswordPrincipalSource):
    pass