[Zope3-checkins] SVN: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/ Initial reorganize the pluggableauth package

Roger Ineichen roger at projekt01.ch
Sat Jul 10 16:45:08 EDT 2004


Log message for revision 26408:
Initial reorganize the pluggableauth package
Move PluggableAuthenticationService code to pluggableauth.py
Move BTreePrincipalSource code to gtreesource.py
Move SimplePrincipalcode to principal.py
Move PrincipalAuthenticationView to a browser package
Reorganize Interfaces and imports
Added IPluggableAuthentication for to provide a constraints
Remove dependency to BTreePrincipalSource
Added IPrincipalSourceContained interface


-=-
Modified: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/__init__.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/__init__.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/__init__.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -15,582 +15,11 @@
 
 $Id$
 """
-import random
-import sys
-import time
-import random
-import zope.schema
+from exception import LoginNameTaken
+from principal import SimplePrincipal
+from interfaces import IBTreePrincipalSource
+from interfaces import IBTreePrincipalSourceContained
+from btreesource import BTreePrincipalSource
+from pluggableauth import PluggableAuthenticationService
+from pluggableauth import PluggableAuthenticationServiceAddSubscriber
 
-from warnings import warn
-from persistent import Persistent
-from BTrees.IOBTree import IOBTree
-from BTrees.OIBTree import OIBTree
-
-from zope.interface import implements
-from zope.component.interfaces import IViewFactory
-from zope.exceptions import NotFoundError
-
-from zope.app import zapi
-from zope.app.location import locate
-from zope.app.traversing.api import getPath
-
-from zope.app.container.interfaces import IOrderedContainer
-from zope.app.container.interfaces import IContainerNamesContainer, INameChooser
-from zope.app.container.interfaces import IContained
-from zope.app.container.constraints import ItemTypePrecondition
-from zope.app.container.constraints import ContainerTypesConstraint
-from zope.app.container.contained import Contained, setitem, uncontained
-from zope.app.container.ordered import OrderedContainer
-
-from zope.app.servicenames import Authentication
-from zope.app.security.interfaces import ILoginPassword
-from zope.app.site.interfaces import ISimpleService
-from zope.app.component.localservice import queryNextService
-
-from interfaces import IUserSchemafied, IPluggableAuthenticationService
-from interfaces import \
-     IPrincipalSource, ILoginPasswordPrincipalSource, IContainerPrincipalSource
-
-def gen_key():
-    """Return a random int (1, MAXINT), suitable for use as a BTree key."""
-
-    return random.randint(0, sys.maxint-1)
-
-class PluggableAuthenticationService(OrderedContainer):
-
-    implements(IPluggableAuthenticationService, ISimpleService,
-               IOrderedContainer)
-
-    def __init__(self, earmark=None):
-        self.earmark = earmark
-        # The earmark is used as a token which can uniquely identify
-        # this authentication service instance even if the service moves
-        # from place to place within the same context chain or is renamed.
-        # It is included in principal ids of principals which are obtained
-        # from this auth service, so code which dereferences a principal
-        # (like getPrincipal of this auth service) needs to take the earmark
-        # into account. The earmark cannot change once it is assigned.  If it
-        # does change, the system will not be able to dereference principal
-        # references which embed the old earmark.
-        OrderedContainer.__init__(self)
-
-    def authenticate(self, request):
-        """ See IAuthenticationService. """
-        for ps_key, ps in self.items():
-            loginView = zapi.queryView(ps, "login", request)
-            if loginView is not None:
-                principal = loginView.authenticate()
-                if principal is not None:
-                    return principal
-
-        next = queryNextService(self, Authentication, None)
-        if next is not None:
-            return next.authenticate(request)
-
-        return None
-
-    def unauthenticatedPrincipal(self):
-        # It's safe to assume that the global auth service will
-        # provide an unauthenticated principal, so we won't bother.
-        return None
-
-    def unauthorized(self, id, request):
-        """ See IAuthenticationService. """
-
-        next = queryNextService(self, Authentication, None)
-        if next is not None:
-            return next.unauthorized(id, request)
-
-        return None
-
-    def getPrincipal(self, id):
-        """ See IAuthenticationService.
-
-        For this implementation, an 'id' is a string which can be
-        split into a 3-tuple by splitting on tab characters.  The
-        three tuple consists of (auth_service_earmark,
-        principal_source_id, principal_id).
-
-        In the current strategy, the principal sources that are members
-        of this authentication service cannot be renamed; if they are,
-        principal references that embed the old name will not be
-        dereferenceable.
-
-        """
-
-        next = None
-
-        try:
-            auth_svc_earmark, principal_src_id, principal_id = id.split('\t',2)
-        except (TypeError, ValueError, AttributeError):
-            auth_svc_earmark, principal_src_id, principal_id = None, None, None
-            next = queryNextService(self, Authentication, None)
-
-        if auth_svc_earmark != self.earmark:
-            # this is not our reference because its earmark doesnt match ours
-            next = queryNextService(self, Authentication, None)
-
-        if next is not None:
-            return next.getPrincipal(id)
-
-        source = self.get(principal_src_id)
-        if source is None:
-            raise NotFoundError, principal_src_id
-        return source.getPrincipal(id)
-
-    def getPrincipals(self, name):
-        """ See IAuthenticationService. """
-
-        for ps_key, ps in self.items():
-            for p in ps.getPrincipals(name):
-                yield p
-
-        next = queryNextService(self, Authentication, None)
-        if next is not None:
-            for p in next.getPrincipals(name):
-                yield p
-
-    def addPrincipalSource(self, id, principal_source):
-        """ See IPluggableAuthenticationService.
-
-        >>> pas = PluggableAuthenticationService()
-        >>> sps = BTreePrincipalSource()
-        >>> pas.addPrincipalSource('simple', sps)
-        >>> sps2 = BTreePrincipalSource()
-        >>> pas.addPrincipalSource('not_quite_so_simple', sps2)
-        >>> pas.keys()
-        ['simple', 'not_quite_so_simple']
-        """
-
-        if not IPrincipalSource.providedBy(principal_source):
-            raise TypeError("Source must implement IPrincipalSource")
-        locate(principal_source, self, id)
-        self[id] = principal_source        
-
-    def removePrincipalSource(self, id):
-        """ See IPluggableAuthenticationService.
-
-        >>> pas = PluggableAuthenticationService()
-        >>> sps = BTreePrincipalSource()
-        >>> pas.addPrincipalSource('simple', sps)
-        >>> sps2 = BTreePrincipalSource()
-        >>> pas.addPrincipalSource('not_quite_so_simple', sps2)
-        >>> sps3 = BTreePrincipalSource()
-        >>> pas.addPrincipalSource('simpler', sps3)
-        >>> pas.keys()
-        ['simple', 'not_quite_so_simple', 'simpler']
-        >>> pas.removePrincipalSource('not_quite_so_simple')
-        >>> pas.keys()
-        ['simple', 'simpler']
-        """
-
-        del self[id]
-
-
-def PluggableAuthenticationServiceAddSubscriber(self, event):
-    r"""Generates an earmark if one is not provided.
-
-    Define a stub for PluggableAuthenticationService
-
-    >>> from zope.app.traversing.interfaces import IPhysicallyLocatable
-    >>> class PluggableAuthStub:
-    ...     implements(IPhysicallyLocatable)
-    ...     def __init__(self, earmark=None):
-    ...         self.earmark = earmark
-    ...     def getName(self):
-    ...         return 'PluggableAuthName'
-
-    The subscriber generates an earmark for the auth service if one is not
-    set in the init.
-
-    >>> stub = PluggableAuthStub()
-    >>> event = ''
-    >>> PluggableAuthenticationServiceAddSubscriber(stub, event)
-    >>> stub.earmark is not None
-    True
-
-    The subscriber does not modify an earmark for the auth service if one
-    exists already.
-
-    >>> earmark = 'my sample earmark'
-    >>> stub = PluggableAuthStub(earmark=earmark)
-    >>> event = ''
-    >>> PluggableAuthenticationServiceAddSubscriber(stub, event)
-    >>> stub.earmark == earmark
-    True
-    """
-    if self.earmark is None:
-        # we manufacture what is intended to be a globally unique
-        # earmark if one is not provided in __init__
-        myname = zapi.name(self)
-        rand_id = gen_key()
-        t = int(time.time())
-        self.earmark = '%s-%s-%s' % (myname, rand_id, t)
-                
-
-class IBTreePrincipalSource(
-    ILoginPasswordPrincipalSource,
-    IContainerPrincipalSource,
-    INameChooser,
-    IContainerNamesContainer,
-    ):
-
-    def __setitem__(name, principal):
-        """Add a principal
-
-        The name must be the same as the principal login
-        """
-
-    __setitem__.precondition  = ItemTypePrecondition(IUserSchemafied)
-
-class IBTreePrincipalSourceContained(IContained):
-
-    __parent__ = zope.schema.Field(
-        constraint = ContainerTypesConstraint(IBTreePrincipalSource),
-        )
-
-class BTreePrincipalSource(Persistent, Contained):
-    """An efficient, scalable provider of Authentication Principals."""
-
-    implements(IBTreePrincipalSource)
-
-    def __init__(self):
-
-        self._principals_by_number = IOBTree()
-        self._numbers_by_login = OIBTree()
-
-    # IContainer-related methods
-
-    def __delitem__(self, login):
-        """ See IContainer.
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal('fred', 'fred', '123')
-        >>> sps['fred'] = prin
-        >>> int(sps.get('fred') == prin)
-        1
-        >>> del sps['fred']
-        >>> int(sps.get('fred') == prin)
-        0
-
-        """
-        number = self._numbers_by_login[login]
-
-        uncontained(self._principals_by_number[number], self, login)
-        del self._principals_by_number[number]
-        del self._numbers_by_login[login]
-
-    def __setitem__(self, login, ob):
-        """ See IContainerNamesContainer
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal('gandalf', 'shadowfax')
-        >>> sps['doesntmatter'] = prin
-        >>> sps.get('doesntmatter')
-        """
-        setitem(self, self.__setitem, login, ob)
-
-    def __setitem(self, login, ob):
-        store = self._principals_by_number
-
-        key = gen_key()
-        while not store.insert(key, ob):
-            key = gen_key()
-
-        ob.id = key
-        self._numbers_by_login[ob.login] = key
-
-    def keys(self):
-        """ See IContainer.
-
-        >>> sps = BTreePrincipalSource()
-        >>> sps.keys()
-        []
-        >>> prin = SimplePrincipal('arthur', 'tea')
-        >>> sps['doesntmatter'] = prin
-        >>> sps.keys()
-        ['arthur']
-        >>> prin = SimplePrincipal('ford', 'towel')
-        >>> sps['doesntmatter'] = prin
-        >>> sps.keys()
-        ['arthur', 'ford']
-        """
-
-        return list(self._numbers_by_login.keys())
-
-    def __iter__(self):
-        """ See IContainer.
-
-        >>> sps = BTreePrincipalSource()
-        >>> sps.keys()
-        []
-        >>> prin = SimplePrincipal('trillian', 'heartOfGold')
-        >>> sps['doesntmatter'] = prin
-        >>> prin = SimplePrincipal('zaphod', 'gargleblaster')
-        >>> sps['doesntmatter'] = prin
-        >>> [i for i in sps]
-        ['trillian', 'zaphod']
-        """
-
-        return iter(self.keys())
-
-    def __getitem__(self, key):
-        """ See IContainer
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal('gag', 'justzisguy')
-        >>> sps['doesntmatter'] = prin
-        >>> sps['gag'].login
-        'gag'
-        """
-
-        number = self._numbers_by_login[key]
-        return self._principals_by_number[number]
-
-    def get(self, key, default=None):
-        """ See IContainer
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal(1, 'slartibartfast', 'fjord')
-        >>> sps['slartibartfast'] = prin
-        >>> principal = sps.get('slartibartfast')
-        >>> sps.get('marvin', 'No chance, dude.')
-        'No chance, dude.'
-        """
-
-        try:
-            number = self._numbers_by_login[key]
-        except KeyError:
-            return default
-
-        return self._principals_by_number[number]
-
-    def values(self):
-        """ See IContainer.
-
-        >>> sps = BTreePrincipalSource()
-        >>> sps.keys()
-        []
-        >>> prin = SimplePrincipal('arthur', 'tea')
-        >>> sps['doesntmatter'] = prin
-        >>> [user.login for user in sps.values()]
-        ['arthur']
-        >>> prin = SimplePrincipal('ford', 'towel')
-        >>> sps['doesntmatter'] = prin
-        >>> [user.login for user in sps.values()]
-        ['arthur', 'ford']
-        """
-
-        return [self._principals_by_number[n]
-                for n in self._numbers_by_login.values()]
-
-    def __len__(self):
-        """ See IContainer
-
-        >>> sps = BTreePrincipalSource()
-        >>> int(len(sps) == 0)
-        1
-        >>> prin = SimplePrincipal(1, 'trillian', 'heartOfGold')
-        >>> sps['trillian'] = prin
-        >>> int(len(sps) == 1)
-        1
-        """
-
-        return len(self._principals_by_number)
-
-    def items(self):
-        """ See IContainer.
-
-        >>> sps = BTreePrincipalSource()
-        >>> sps.keys()
-        []
-        >>> prin = SimplePrincipal('zaphod', 'gargleblaster')
-        >>> sps['doesntmatter'] = prin
-        >>> [(k, v.login) for k, v in sps.items()]
-        [('zaphod', 'zaphod')]
-        >>> prin = SimplePrincipal('marvin', 'paranoid')
-        >>> sps['doesntmatter'] = prin
-        >>> [(k, v.login) for k, v in sps.items()]
-        [('marvin', 'marvin'), ('zaphod', 'zaphod')]
-        """
-
-        # We're being expensive here (see values() above) for convenience
-        return [(p.login, p) for p in self.values()]
-
-    def __contains__(self, key):
-        """ See IContainer.
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal('slinkp', 'password')
-        >>> sps['doesntmatter'] = prin
-        >>> int('slinkp' in sps)
-        1
-        >>> int('desiato' in sps)
-        0
-        """
-        return self._numbers_by_login.has_key(key)
-
-    has_key = __contains__
-
-    # PrincipalSource-related methods
-
-    def getPrincipal(self, id):
-        """ See IPrincipalSource.
-
-        'id' is the id as returned by principal.getId(),
-        not a login.
-
-        """
-
-        id = id.split('\t')[2]
-        id = int(id)
-
-        try:
-            return self._principals_by_number[id]
-        except KeyError:
-            raise NotFoundError, id
-
-    def getPrincipals(self, name):
-        """ See IPrincipalSource.
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin1 = SimplePrincipal('gandalf', 'shadowfax')
-        >>> sps['doesntmatter'] = prin1
-        >>> prin1 = SimplePrincipal('frodo', 'ring')
-        >>> sps['doesntmatter'] = prin1
-        >>> prin1 = SimplePrincipal('pippin', 'pipe')
-        >>> sps['doesntmatter'] = prin1
-        >>> prin1 = SimplePrincipal('sam', 'garden')
-        >>> sps['doesntmatter'] = prin1
-        >>> prin1 = SimplePrincipal('merry', 'food')
-        >>> sps['doesntmatter'] = prin1
-        >>> [p.login for p in sps.getPrincipals('a')]
-        ['gandalf', 'sam']
-        >>> [p.login for p in sps.getPrincipals('')]
-        ['frodo', 'gandalf', 'merry', 'pippin', 'sam']
-        >>> [p.login for p in sps.getPrincipals('sauron')]
-        []
-        """
-
-        for k in self.keys():
-            if k.find(name) != -1:
-                yield self[k]
-
-    def authenticate(self, login, password):
-        """ See ILoginPasswordPrincipalSource. """
-        number = self._numbers_by_login.get(login)
-        if number is None:
-            return
-        user = self._principals_by_number[number]
-        if user.password == password:
-            return user
-
-
-    def checkName(self, name, object):
-        """Check to make sure the name is valid
-
-        Don't allow suplicate names:
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin1 = SimplePrincipal('gandalf', 'shadowfax')
-        >>> sps['gandalf'] = prin1
-        >>> sps.checkName('gandalf', prin1)
-        Traceback (most recent call last):
-        ...
-        LoginNameTaken: gandalf
-
-        """
-        if name in self._numbers_by_login:
-            raise LoginNameTaken(name)
-
-    def chooseName(self, name, object):
-        """Choose a name for the principal
-
-        Always choose the object's existing name:
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin1 = SimplePrincipal('gandalf', 'shadowfax')
-        >>> sps.chooseName(None, prin1)
-        'gandalf'
-
-        """
-        return object.login
-
-class LoginNameTaken(Exception):
-    """A login name is in use
-    """
-
-
-class SimplePrincipal(Persistent, Contained):
-    """A no-frills IUserSchemafied implementation."""
-
-    implements(IUserSchemafied, IBTreePrincipalSourceContained)
-
-    def __init__(self, login, password, title='', description=''):
-        self._id = ''
-        self.login = login
-        self.password = password
-        self.title = title
-        self.description = description
-
-    def _getId(self):
-        source = self.__parent__
-        auth = source.__parent__
-        return "%s\t%s\t%s" %(auth.earmark, source.__name__, self._id)
-
-    def _setId(self, id):
-        self._id = id
-
-    id = property(_getId, _setId)
-
-    def getTitle(self):
-        warn("Use principal.title instead of principal.getTitle().",
-             DeprecationWarning, 2)
-        return self.title
-
-    def getDescription(self):
-        warn("Use principal.description instead of principal.getDescription().",
-             DeprecationWarning, 2)
-        return self.description
-
-    def getLogin(self):
-        """See IReadUser."""
-        return self.login
-
-    def validate(self, test_password):
-        """ See IReadUser.
-
-        >>> pal = SimplePrincipal('gandalf', 'shadowfax', 'The Grey Wizard',
-        ...                       'Cool old man with neato fireworks. '
-        ...                       'Has a nice beard.')
-        >>> pal.validate('shdaowfax')
-        False
-        >>> pal.validate('shadowfax')
-        True
-        """
-        return test_password == self.password
-
-class PrincipalAuthenticationView:
-    """Simple basic authentication view
-
-    This only handles requests which have basic auth credentials
-    in them currently (ILoginPassword-based requests).
-    If you want a different policy, you'll need to write and register
-    a different view, replacing this one.
-    
-    """
-    implements(IViewFactory)
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-
-    def authenticate(self):
-        a = ILoginPassword(self.request, None)
-        if a is None:
-            return
-        login = a.getLogin()
-        password = a.getPassword()
-
-        p = self.context.authenticate(login, password)
-        return p

Modified: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/__init__.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/__init__.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/__init__.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -1 +1,2 @@
 #
+#from zope.app.pluggableauth.browser.authentication import PrincipalAuthenticationView

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/authentication.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/authentication.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/authentication.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,31 @@
+#
+from zope.interface import implements
+from zope.component.interfaces import IViewFactory
+from zope.app.security.interfaces import ILoginPassword
+
+
+
+class PrincipalAuthenticationView:
+    """Simple basic authentication view
+
+    This only handles requests which have basic auth credentials
+    in them currently (ILoginPassword-based requests).
+    If you want a different policy, you'll need to write and register
+    a different view, replacing this one.
+    
+    """
+    implements(IViewFactory)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def authenticate(self):
+        a = ILoginPassword(self.request, None)
+        if a is None:
+            return
+        login = a.getLogin()
+        password = a.getPassword()
+
+        p = self.context.authenticate(login, password)
+        return p


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/authentication.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/configure.zcml
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/configure.zcml	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/configure.zcml	2004-07-10 20:45:08 UTC (rev 26408)
@@ -2,6 +2,14 @@
    xmlns="http://namespaces.zope.org/browser"
    xmlns:zope="http://namespaces.zope.org/zope">
 
+<!-- Login view -->
+
+  <view
+      name="login"
+      for="zope.app.pluggableauth.interfaces.ILoginPasswordPrincipalSource"
+      class=".authentication.PrincipalAuthenticationView"
+      permission="zope.Public" />
+
 <!-- Pluggable Authentication Service -->
 
   <addMenuItem

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/__init__.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/__init__.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/__init__.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/__init__.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/test_authentication.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/test_authentication.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/test_authentication.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# 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.1 (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.
+#
+##############################################################################
+"""Pluggable Auth Browser Tests
+
+$Id: test_authentication.py 25177 2004-06-02 13:17:31Z jim $
+"""
+from unittest import TestCase, TestSuite, main, makeSuite
+from zope.app.pluggableauth.browser.authentication import \
+    PrincipalAuthenticationView
+from zope.app.pluggableauth.tests.authsetup import AuthSetup
+
+
+
+class PrincipalAuthenticationViewTest(AuthSetup, TestCase):
+
+    def test_authenticate(self):
+        request = self.getRequest('srichter', 'hello')
+        view = PrincipalAuthenticationView(self._one, request)
+        self.assertEqual(self._srichter, view.authenticate())
+
+
+def test_suite():
+    t1 = makeSuite(PrincipalAuthenticationViewTest)
+    return TestSuite((t1,))
+
+
+if __name__=='__main__':
+    main(defaultTest='test_suite')
+


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/browser/tests/test_authentication.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/btreesource.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/btreesource.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/btreesource.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,309 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Pluggable Authentication service implementation.
+
+$Id: __init__.py 26176 2004-07-07 18:34:31Z jim $
+"""
+import sys
+import random
+
+from persistent import Persistent
+from BTrees.IOBTree import IOBTree
+from BTrees.OIBTree import OIBTree
+
+from zope.interface import implements
+from zope.exceptions import NotFoundError
+
+from zope.app.container.contained import Contained, setitem, uncontained
+
+from interfaces import IContainerPrincipalSource
+from interfaces import IBTreePrincipalSource
+
+from exception import LoginNameTaken
+from principal import SimplePrincipal
+
+
+
+def gen_key():
+    """Return a random int (1, MAXINT), suitable for use as a BTree key."""
+
+    return random.randint(0, sys.maxint-1)
+
+
+class BTreePrincipalSource(Persistent, Contained):
+    """An efficient, scalable provider of Authentication Principals."""
+
+    implements(IBTreePrincipalSource)
+
+    def __init__(self):
+
+        self._principals_by_number = IOBTree()
+        self._numbers_by_login = OIBTree()
+
+    # IContainer-related methods
+
+    def __delitem__(self, login):
+        """ See IContainer.
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin = SimplePrincipal('fred', 'fred', '123')
+        >>> sps['fred'] = prin
+        >>> int(sps.get('fred') == prin)
+        1
+        >>> del sps['fred']
+        >>> int(sps.get('fred') == prin)
+        0
+
+        """
+        number = self._numbers_by_login[login]
+
+        uncontained(self._principals_by_number[number], self, login)
+        del self._principals_by_number[number]
+        del self._numbers_by_login[login]
+
+    def __setitem__(self, login, ob):
+        """ See IContainerNamesContainer
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin = SimplePrincipal('gandalf', 'shadowfax')
+        >>> sps['doesntmatter'] = prin
+        >>> sps.get('doesntmatter')
+        """
+        setitem(self, self.__setitem, login, ob)
+
+    def __setitem(self, login, ob):
+        store = self._principals_by_number
+
+        key = gen_key()
+        while not store.insert(key, ob):
+            key = gen_key()
+
+        ob.id = key
+        self._numbers_by_login[ob.login] = key
+
+    def keys(self):
+        """ See IContainer.
+
+        >>> sps = BTreePrincipalSource()
+        >>> sps.keys()
+        []
+        >>> prin = SimplePrincipal('arthur', 'tea')
+        >>> sps['doesntmatter'] = prin
+        >>> sps.keys()
+        ['arthur']
+        >>> prin = SimplePrincipal('ford', 'towel')
+        >>> sps['doesntmatter'] = prin
+        >>> sps.keys()
+        ['arthur', 'ford']
+        """
+
+        return list(self._numbers_by_login.keys())
+
+    def __iter__(self):
+        """ See IContainer.
+
+        >>> sps = BTreePrincipalSource()
+        >>> sps.keys()
+        []
+        >>> prin = SimplePrincipal('trillian', 'heartOfGold')
+        >>> sps['doesntmatter'] = prin
+        >>> prin = SimplePrincipal('zaphod', 'gargleblaster')
+        >>> sps['doesntmatter'] = prin
+        >>> [i for i in sps]
+        ['trillian', 'zaphod']
+        """
+
+        return iter(self.keys())
+
+    def __getitem__(self, key):
+        """ See IContainer
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin = SimplePrincipal('gag', 'justzisguy')
+        >>> sps['doesntmatter'] = prin
+        >>> sps['gag'].login
+        'gag'
+        """
+
+        number = self._numbers_by_login[key]
+        return self._principals_by_number[number]
+
+    def get(self, key, default=None):
+        """ See IContainer
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin = SimplePrincipal(1, 'slartibartfast', 'fjord')
+        >>> sps['slartibartfast'] = prin
+        >>> principal = sps.get('slartibartfast')
+        >>> sps.get('marvin', 'No chance, dude.')
+        'No chance, dude.'
+        """
+
+        try:
+            number = self._numbers_by_login[key]
+        except KeyError:
+            return default
+
+        return self._principals_by_number[number]
+
+    def values(self):
+        """ See IContainer.
+
+        >>> sps = BTreePrincipalSource()
+        >>> sps.keys()
+        []
+        >>> prin = SimplePrincipal('arthur', 'tea')
+        >>> sps['doesntmatter'] = prin
+        >>> [user.login for user in sps.values()]
+        ['arthur']
+        >>> prin = SimplePrincipal('ford', 'towel')
+        >>> sps['doesntmatter'] = prin
+        >>> [user.login for user in sps.values()]
+        ['arthur', 'ford']
+        """
+
+        return [self._principals_by_number[n]
+                for n in self._numbers_by_login.values()]
+
+    def __len__(self):
+        """ See IContainer
+
+        >>> sps = BTreePrincipalSource()
+        >>> int(len(sps) == 0)
+        1
+        >>> prin = SimplePrincipal(1, 'trillian', 'heartOfGold')
+        >>> sps['trillian'] = prin
+        >>> int(len(sps) == 1)
+        1
+        """
+
+        return len(self._principals_by_number)
+
+    def items(self):
+        """ See IContainer.
+
+        >>> sps = BTreePrincipalSource()
+        >>> sps.keys()
+        []
+        >>> prin = SimplePrincipal('zaphod', 'gargleblaster')
+        >>> sps['doesntmatter'] = prin
+        >>> [(k, v.login) for k, v in sps.items()]
+        [('zaphod', 'zaphod')]
+        >>> prin = SimplePrincipal('marvin', 'paranoid')
+        >>> sps['doesntmatter'] = prin
+        >>> [(k, v.login) for k, v in sps.items()]
+        [('marvin', 'marvin'), ('zaphod', 'zaphod')]
+        """
+
+        # We're being expensive here (see values() above) for convenience
+        return [(p.login, p) for p in self.values()]
+
+    def __contains__(self, key):
+        """ See IContainer.
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin = SimplePrincipal('slinkp', 'password')
+        >>> sps['doesntmatter'] = prin
+        >>> int('slinkp' in sps)
+        1
+        >>> int('desiato' in sps)
+        0
+        """
+        return self._numbers_by_login.has_key(key)
+
+    has_key = __contains__
+
+    # PrincipalSource-related methods
+
+    def getPrincipal(self, id):
+        """ See IPrincipalSource.
+
+        'id' is the id as returned by principal.getId(),
+        not a login.
+
+        """
+
+        id = id.split('\t')[2]
+        id = int(id)
+
+        try:
+            return self._principals_by_number[id]
+        except KeyError:
+            raise NotFoundError, id
+
+    def getPrincipals(self, name):
+        """ See IPrincipalSource.
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin1 = SimplePrincipal('gandalf', 'shadowfax')
+        >>> sps['doesntmatter'] = prin1
+        >>> prin1 = SimplePrincipal('frodo', 'ring')
+        >>> sps['doesntmatter'] = prin1
+        >>> prin1 = SimplePrincipal('pippin', 'pipe')
+        >>> sps['doesntmatter'] = prin1
+        >>> prin1 = SimplePrincipal('sam', 'garden')
+        >>> sps['doesntmatter'] = prin1
+        >>> prin1 = SimplePrincipal('merry', 'food')
+        >>> sps['doesntmatter'] = prin1
+        >>> [p.login for p in sps.getPrincipals('a')]
+        ['gandalf', 'sam']
+        >>> [p.login for p in sps.getPrincipals('')]
+        ['frodo', 'gandalf', 'merry', 'pippin', 'sam']
+        >>> [p.login for p in sps.getPrincipals('sauron')]
+        []
+        """
+
+        for k in self.keys():
+            if k.find(name) != -1:
+                yield self[k]
+
+    def authenticate(self, login, password):
+        """ See ILoginPasswordPrincipalSource. """
+        number = self._numbers_by_login.get(login)
+        if number is None:
+            return
+        user = self._principals_by_number[number]
+        if user.password == password:
+            return user
+
+
+    def checkName(self, name, object):
+        """Check to make sure the name is valid
+
+        Don't allow suplicate names:
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin1 = SimplePrincipal('gandalf', 'shadowfax')
+        >>> sps['gandalf'] = prin1
+        >>> sps.checkName('gandalf', prin1)
+        Traceback (most recent call last):
+        ...
+        LoginNameTaken: gandalf
+
+        """
+        if name in self._numbers_by_login:
+            raise LoginNameTaken(name)
+
+    def chooseName(self, name, object):
+        """Choose a name for the principal
+
+        Always choose the object's existing name:
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin1 = SimplePrincipal('gandalf', 'shadowfax')
+        >>> sps.chooseName(None, prin1)
+        'gandalf'
+
+        """
+        return object.login


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/btreesource.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/configure.zcml
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/configure.zcml	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/configure.zcml	2004-07-10 20:45:08 UTC (rev 26408)
@@ -62,12 +62,6 @@
         />
   </content>
 
-  <browser:view
-      name="login"
-      for=".interfaces.ILoginPasswordPrincipalSource"
-      class="zope.app.pluggableauth.PrincipalAuthenticationView"
-      permission="zope.Public" />
-
   <include package=".browser" />
 
 </configure>

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/exception.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/exception.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/exception.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Pluggable Authentication service implementation.
+
+$Id: exception.py 26176 2004-07-07 18:34:31Z jim $
+"""
+class LoginNameTaken(Exception):
+    """Exception for to raise if a login name is allready in use."""


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/exception.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/interfaces.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/interfaces.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/interfaces.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -17,12 +17,16 @@
 """
 from zope.app.i18n import ZopeMessageIDFactory as _
 from zope.app.container.interfaces import IContainer, IContained
+from zope.app.container.interfaces import IContainerNamesContainer, INameChooser
 from zope.app.container.constraints import ItemTypePrecondition
 from zope.app.container.constraints import ContainerTypesConstraint
 from zope.app.security.interfaces import IAuthenticationService, IPrincipal
 from zope.interface import Interface
+import zope.schema
 from zope.schema import Text, TextLine, Password, Field
 
+
+
 class IUserSchemafied(IPrincipal):
     """A User object with schema-defined attributes."""
 
@@ -41,10 +45,17 @@
         """Confirm whether 'password' is the password of the user."""
 
 
-class IPrincipalSource(Interface):
-    """A read-only source of IPrincipals.
+class IPluggableAuthentication(Interface):
+    """A marker for to mix in a constraints."""
+
+
+class IPrincipalSource(IContained):
+    """A read-only source of IPrincipals where can be added to a auth service.
     """
 
+    __parent__= Field(
+        constraint = ContainerTypesConstraint(IPluggableAuthentication))
+
     def getPrincipal(id):
         """Get principal meta-data.
 
@@ -72,8 +83,29 @@
         """
 
 
-class IPluggableAuthenticationService(IAuthenticationService, IContainer):
+class ILoginPasswordPrincipalSource(IPrincipalSource):
+    """ A principal source which can authenticate a user given a
+    login and a password """
+
+    def authenticate(login, password):
+        """ Return a principal matching the login/password pair.
+
+        If there is no principal in this principal source which
+        matches the login/password pair, return None.
+
+        Note: A login is different than an id.  Principals may have
+        logins that differ from their id.  For example, a user may
+        have a login which is his email address.  He'd like to be able
+        to change his login when his email address changes without
+        effecting his security profile on the site.
+        """
+
+
+class IPluggableAuthenticationService(IPluggableAuthentication, \
+                                      IAuthenticationService, IContainer):
     """An AuthenticationService that can contain multiple pricipal sources.
+    
+    Inherit from IPluggableAuthentication for to provide a constraints.
     """
 
     def __setitem__(id, principal_source):
@@ -87,26 +119,43 @@
         """
 
 
-class ILoginPasswordPrincipalSource(IPrincipalSource):
-    """ A principal source which can authenticate a user given a
-    login and a password """
+class IContainerPrincipalSource(IPrincipalSource):
+    """This is a marker interface for specifying principal sources that are
+    also containers. """
 
-    def authenticate(login, password):
-        """ Return a principal matching the login/password pair.
 
-        If there is no principal in this principal source which
-        matches the login/password pair, return None.
+class IPrincipalSourceContained(IContained):
+    """Make shure we just let object add to IPrincipalSource 
+    porvided instances. """
 
-        Note: A login is different than an id.  Principals may have
-        logins that differ from their id.  For example, a user may
-        have a login which is his email address.  He'd like to be able
-        to change his login when his email address changes without
-        effecting his security profile on the site.  """
+    __parent__ = zope.schema.Field(
+        constraint = ContainerTypesConstraint(IPrincipalSource),
+        )
 
 
-class IContainerPrincipalSource(IPrincipalSource, IContained):
-    """This is a marker interface for specifying principal sources that are
-    also containers. """
+class IBTreePrincipalSource(
+    ILoginPasswordPrincipalSource,
+    IContainerPrincipalSource,
+    INameChooser,
+    IContainerNamesContainer,
+    ):
 
-    __parent__= Field(
-        constraint = ContainerTypesConstraint(IPluggableAuthenticationService))
+    def __setitem__(name, principal):
+        """Add a principal
+
+        The name must be the same as the principal login
+        """
+
+    __setitem__.precondition  = ItemTypePrecondition(IUserSchemafied)
+
+
+class IBTreePrincipalSourceContained(IPrincipalSourceContained):
+    """Set own constarints to the BTreePrincipalSource.
+    
+    Make shure we just let object add to IBTreePrinicpalSource 
+    porvided instances.
+    """
+
+    __parent__ = zope.schema.Field(
+        constraint = ContainerTypesConstraint(IBTreePrincipalSource),
+        )
\ No newline at end of file

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,154 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Pluggable Authentication service implementation.
+
+$Id: __init__.py 26176 2004-07-07 18:34:31Z jim $
+"""
+import time
+
+from zope.interface import implements
+from zope.exceptions import NotFoundError
+
+from zope.app import zapi
+from zope.app.location import locate
+
+from zope.app.container.interfaces import IOrderedContainer
+from zope.app.container.ordered import OrderedContainer
+
+from zope.app.servicenames import Authentication
+from zope.app.site.interfaces import ISimpleService
+from zope.app.component.localservice import queryNextService
+
+from interfaces import IPluggableAuthenticationService
+from interfaces import IPrincipalSource
+from btreesource import gen_key
+
+
+class PluggableAuthenticationService(OrderedContainer):
+
+    implements(IPluggableAuthenticationService, ISimpleService,
+               IOrderedContainer)
+
+    def __init__(self, earmark=None):
+        self.earmark = earmark
+        # The earmark is used as a token which can uniquely identify
+        # this authentication service instance even if the service moves
+        # from place to place within the same context chain or is renamed.
+        # It is included in principal ids of principals which are obtained
+        # from this auth service, so code which dereferences a principal
+        # (like getPrincipal of this auth service) needs to take the earmark
+        # into account. The earmark cannot change once it is assigned.  If it
+        # does change, the system will not be able to dereference principal
+        # references which embed the old earmark.
+        OrderedContainer.__init__(self)
+
+    def authenticate(self, request):
+        """ See IAuthenticationService. """
+        for ps_key, ps in self.items():
+            loginView = zapi.queryView(ps, "login", request)
+            if loginView is not None:
+                principal = loginView.authenticate()
+                if principal is not None:
+                    return principal
+
+        next = queryNextService(self, Authentication, None)
+        if next is not None:
+            return next.authenticate(request)
+
+        return None
+
+    def unauthenticatedPrincipal(self):
+        # It's safe to assume that the global auth service will
+        # provide an unauthenticated principal, so we won't bother.
+        return None
+
+    def unauthorized(self, id, request):
+        """ See IAuthenticationService. """
+
+        next = queryNextService(self, Authentication, None)
+        if next is not None:
+            return next.unauthorized(id, request)
+
+        return None
+
+    def getPrincipal(self, id):
+        """ See IAuthenticationService.
+
+        For this implementation, an 'id' is a string which can be
+        split into a 3-tuple by splitting on tab characters.  The
+        three tuple consists of (auth_service_earmark,
+        principal_source_id, principal_id).
+
+        In the current strategy, the principal sources that are members
+        of this authentication service cannot be renamed; if they are,
+        principal references that embed the old name will not be
+        dereferenceable.
+
+        """
+
+        next = None
+
+        try:
+            auth_svc_earmark, principal_src_id, principal_id = id.split('\t',2)
+        except (TypeError, ValueError, AttributeError):
+            auth_svc_earmark, principal_src_id, principal_id = None, None, None
+            next = queryNextService(self, Authentication, None)
+
+        if auth_svc_earmark != self.earmark:
+            # this is not our reference because its earmark doesnt match ours
+            next = queryNextService(self, Authentication, None)
+
+        if next is not None:
+            return next.getPrincipal(id)
+
+        source = self.get(principal_src_id)
+        if source is None:
+            raise NotFoundError, principal_src_id
+        return source.getPrincipal(id)
+
+    def getPrincipals(self, name):
+        """ See IAuthenticationService. """
+
+        for ps_key, ps in self.items():
+            for p in ps.getPrincipals(name):
+                yield p
+
+        next = queryNextService(self, Authentication, None)
+        if next is not None:
+            for p in next.getPrincipals(name):
+                yield p
+
+    def addPrincipalSource(self, id, principal_source):
+        """ See IPluggableAuthenticationService."""
+
+        if not IPrincipalSource.providedBy(principal_source):
+            raise TypeError("Source must implement IPrincipalSource")
+        locate(principal_source, self, id)
+        self[id] = principal_source        
+
+    def removePrincipalSource(self, id):
+        """ See IPluggableAuthenticationService."""
+        del self[id]
+
+
+def PluggableAuthenticationServiceAddSubscriber(self, event):
+    """Generates an earmark if one is not provided."""
+
+    if self.earmark is None:
+        # we manufacture what is intended to be a globally unique
+        # earmark if one is not provided in __init__
+        myname = zapi.name(self)
+        rand_id = gen_key()
+        t = int(time.time())
+        self.earmark = '%s-%s-%s' % (myname, rand_id, t)


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.py
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.txt
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.txt	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.txt	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,77 @@
+==============================
+PluggableAuthenticationService
+==============================
+
+Adapter registries provide a way to register objects that depend on
+one or more interface specifications and provide (perhaps indirectly)
+some interface.  In addition, the registrations have names. (You can
+think of the names as qualifiers of the provided interfaces.)
+
+The term "interface specification" refers both to interfaces and to
+interface declarations, such as declarations of interfaces implemented
+by a class.
+
+
+Let's test the service
+======================
+
+  >>> from zope.interface import implements
+  >>> from zope.app.pluggableauth.btreesource import BTreePrincipalSource
+  >>> from zope.app.pluggableauth.pluggableauth import PluggableAuthenticationService
+  >>> from zope.app.pluggableauth.pluggableauth import PluggableAuthenticationServiceAddSubscriber
+
+Let's look at a simple example, using a pluggableauth service::
+
+  >>> pas = PluggableAuthenticationService()
+  >>> sps = BTreePrincipalSource()
+  >>> pas.addPrincipalSource('simple', sps)
+  >>> sps2 = BTreePrincipalSource()
+  >>> pas.addPrincipalSource('not_quite_so_simple', sps2)
+  >>> pas.keys()
+  ['simple', 'not_quite_so_simple']
+      
+
+remove a principal source
+
+  >>> pas = PluggableAuthenticationService()
+  >>> sps = BTreePrincipalSource()
+  >>> pas.addPrincipalSource('simple', sps)
+  >>> sps2 = BTreePrincipalSource()
+  >>> pas.addPrincipalSource('not_quite_so_simple', sps2)
+  >>> sps3 = BTreePrincipalSource()
+  >>> pas.addPrincipalSource('simpler', sps3)
+  >>> pas.keys()
+  ['simple', 'not_quite_so_simple', 'simpler']
+  >>> pas.removePrincipalSource('not_quite_so_simple')
+  >>> pas.keys()
+  ['simple', 'simpler']
+
+Generates an earmark if one is not provided.
+Define a stub for PluggableAuthenticationService
+
+  >>> from zope.app.traversing.interfaces import IPhysicallyLocatable
+  >>> class PluggableAuthStub:
+  ...     implements(IPhysicallyLocatable)
+  ...     def __init__(self, earmark=None):
+  ...         self.earmark = earmark
+  ...     def getName(self):
+  ...         return 'PluggableAuthName'
+
+  The subscriber generates an earmark for the auth service if one is not
+  set in the init.
+
+  >>> stub = PluggableAuthStub()
+  >>> event = ''
+  >>> PluggableAuthenticationServiceAddSubscriber(stub, event)
+  >>> stub.earmark is not None
+  True
+
+  The subscriber does not modify an earmark for the auth service if one
+  exists already.
+
+  >>> earmark = 'my sample earmark'
+  >>> stub = PluggableAuthStub(earmark=earmark)
+  >>> event = ''
+  >>> PluggableAuthenticationServiceAddSubscriber(stub, event)
+  >>> stub.earmark == earmark
+  True


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/pluggableauth.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/principal.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/principal.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/principal.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Pluggable Authentication service implementation.
+
+$Id: principal.py 26176 2004-07-07 18:34:31Z jim $
+"""
+from warnings import warn
+from persistent import Persistent
+from zope.interface import implements
+from zope.app.container.contained import Contained
+from interfaces import IUserSchemafied
+from interfaces import IPrincipalSourceContained
+
+
+
+class SimplePrincipal(Persistent, Contained):
+    """A no-frills IUserSchemafied implementation."""
+
+    implements(IUserSchemafied, IPrincipalSourceContained)
+
+    def __init__(self, login, password, title='', description=''):
+        self._id = ''
+        self.login = login
+        self.password = password
+        self.title = title
+        self.description = description
+
+    def _getId(self):
+        source = self.__parent__
+        auth = source.__parent__
+        return "%s\t%s\t%s" %(auth.earmark, source.__name__, self._id)
+
+    def _setId(self, id):
+        self._id = id
+
+    id = property(_getId, _setId)
+
+    def getTitle(self):
+        warn("Use principal.title instead of principal.getTitle().",
+             DeprecationWarning, 2)
+        return self.title
+
+    def getDescription(self):
+        warn("Use principal.description instead of principal.getDescription().",
+             DeprecationWarning, 2)
+        return self.description
+
+    def getLogin(self):
+        """See IReadUser."""
+        return self.login
+
+    def validate(self, test_password):
+        """ See IReadUser.
+
+        >>> pal = SimplePrincipal('gandalf', 'shadowfax', 'The Grey Wizard',
+        ...                       'Cool old man with neato fireworks. '
+        ...                       'Has a nice beard.')
+        >>> pal.validate('shdaowfax')
+        False
+        >>> pal.validate('shadowfax')
+        True
+        """
+        return test_password == self.password
+
+


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/principal.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/authsetup.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/authsetup.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/authsetup.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -26,9 +26,10 @@
 from zope.publisher.interfaces.http import IHTTPCredentials
 from zope.app.security.interfaces import ILoginPassword
 from zope.app.security.basicauthadapter import BasicAuthAdapter
-from zope.app.pluggableauth import \
-     PrincipalAuthenticationView, PluggableAuthenticationService, \
+from zope.app.pluggableauth import PluggableAuthenticationService, \
      BTreePrincipalSource, SimplePrincipal
+from zope.app.pluggableauth.browser.authentication import \
+    PrincipalAuthenticationView
 from zope.app.pluggableauth.interfaces import IPrincipalSource
 
 class AuthSetup(placefulsetup.PlacefulSetup):

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_btreesource.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_btreesource.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_btreesource.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# 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.1 (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.
+#
+##############################################################################
+"""Pluggable Auth Tests
+
+$Id: test_source.py 25177 2004-06-02 13:17:31Z jim $
+"""
+from unittest import TestCase, TestSuite, main, makeSuite
+from zope.testing.doctestunit import DocTestSuite
+from zope.app.tests.placelesssetup import setUp, tearDown
+
+
+def test_suite():
+    t1 = DocTestSuite('zope.app.pluggableauth.btreesource',
+                      setUp=setUp, tearDown=tearDown)
+    return TestSuite((t1,))
+
+
+if __name__=='__main__':
+    main(defaultTest='test_suite')
\ No newline at end of file


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_btreesource.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_pluggableauth.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_pluggableauth.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_pluggableauth.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -28,15 +28,16 @@
 from zope.app.tests import setup
 from zope.exceptions import NotFoundError
 
-from zope.app.pluggableauth import BTreePrincipalSource, \
-     SimplePrincipal, PluggableAuthenticationService, \
-     PrincipalAuthenticationView
-from zope.app.pluggableauth.interfaces import IPrincipalSource
-
-from zope.app.pluggableauth.interfaces import IUserSchemafied
 from zope.app.security.interfaces import IPrincipal, ILoginPassword
 from zope.app.security.basicauthadapter import BasicAuthAdapter
 
+from zope.app.pluggableauth.interfaces import IUserSchemafied
+from zope.app.pluggableauth.interfaces import IPrincipalSource
+from zope.app.pluggableauth import BTreePrincipalSource, \
+     SimplePrincipal, PluggableAuthenticationService
+from zope.app.pluggableauth.browser.authentication import \
+    PrincipalAuthenticationView
+
 from zope.publisher.browser import TestRequest as Request
 
 from zope.app.tests.placelesssetup import setUp, tearDown
@@ -151,21 +152,16 @@
         p = self._slinkp
         self.assertEqual(p, one.getPrincipal(p.id))
 
-class PrincipalAuthenticationViewTest(Setup):
 
-    def test_authenticate(self):
-        request = self.getRequest('chrism', '123')
-        view = PrincipalAuthenticationView(self._one, request)
-        self.assertEqual(self._chrism, view.authenticate())
-
-
 def test_suite():
+    import zope.testing.doctestunit as doctestunit
+    import zope.app.pluggableauth as package
     t1 = makeSuite(AuthServiceTest)
-    t2 = DocTestSuite('zope.app.pluggableauth',
-                      setUp=setUp, tearDown=tearDown)
+    t2 = doctestunit.DocFileSuite(package, 'pluggableauth.txt')
+    #t2 = DocTestSuite('zope.app.pluggableauth.pluggableauth.txt',
+    #                  setUp=setUp, tearDown=tearDown)
     t3 = makeSuite(BTreePrincipalSourceTest)
-    t4 = makeSuite(PrincipalAuthenticationViewTest)
-    return TestSuite((t1, t2, t3, t4))
+    return TestSuite((t1, t2, t3))
 
 
 if __name__=='__main__':

Added: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_principal.py
===================================================================
--- Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_principal.py	2004-07-10 17:45:38 UTC (rev 26407)
+++ Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_principal.py	2004-07-10 20:45:08 UTC (rev 26408)
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# 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.1 (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.
+#
+##############################################################################
+"""Pluggable Auth Tests
+
+$Id: test_principal.py 25177 2004-06-02 13:17:31Z jim $
+"""
+from unittest import TestCase, TestSuite, main, makeSuite
+from zope.testing.doctestunit import DocTestSuite
+from zope.app.tests.placelesssetup import setUp, tearDown
+
+
+def test_suite():
+    t1 = DocTestSuite('zope.app.pluggableauth.principal',
+                      setUp=setUp, tearDown=tearDown)
+    return TestSuite((t1,))
+
+
+if __name__=='__main__':
+    main(defaultTest='test_suite')
+


Property changes on: Zope3/branches/roger-ldapauth/src/zope/app/pluggableauth/tests/test_principal.py
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list