[Zope3-checkins] CVS: Zope3/src/zope/app/services/pluggableauth - __init__.py:1.1.2.4

Chris McDonough chrism@zope.com
Mon, 23 Jun 2003 16:18:05 -0400


Update of /cvs-repository/Zope3/src/zope/app/services/pluggableauth
In directory cvs.zope.org:/tmp/cvs-serv8385/src/zope/app/services/pluggableauth

Modified Files:
      Tag: pluggable_authentication_service-branch
	__init__.py 
Log Message:
Change earmark style.


=== Zope3/src/zope/app/services/pluggableauth/__init__.py 1.1.2.3 => 1.1.2.4 ===
--- Zope3/src/zope/app/services/pluggableauth/__init__.py:1.1.2.3	Sun Jun 22 21:08:22 2003
+++ Zope3/src/zope/app/services/pluggableauth/__init__.py	Mon Jun 23 16:17:34 2003
@@ -25,6 +25,8 @@
 from zodb.btrees.OIBTree import OIBTree
 from zope.interface import implements
 from zope.component import getAdapter, queryAdapter
+from zope.context.wrapper import Wrapper
+from zope.context import getWrapperData
 from zope.app.services.servicenames import Authentication
 from zope.component.interfaces import IViewFactory
 from zope.app.container.btree import BTreeContainer
@@ -38,13 +40,15 @@
 from zope.app.interfaces.services.pluggableauth \
      import IPluggableAuthenticationService
 from zope.app.interfaces.services.pluggableauth import IPrincipalSource
-from zope.app.interfaces.services.pluggableauth import IReadPrincipalSource
+from zope.app.interfaces.services.pluggableauth import IReadPrincipalSource,\
+     ILoginPasswordPrincipalSource, IWritePrincipalSource
 from zope.app.interfaces.services.service import ISimpleService
 from zope.app.component.nextservice import queryNextService
 from zope.app.zapi import queryView
 from zope.app.traversing import getPath
 from zope.context import ContextMethod
 from zope.exceptions import NotFoundError
+import random
 
 def gen_key():
     """Return a random int (1, MAXINT), suitable for use as a BTree key."""
@@ -56,16 +60,18 @@
     implements(IPluggableAuthenticationService, ISimpleService,
                IOrderedContainer, IAddNotifiable)
 
-    def __init__(self):
-
+    def __init__(self, earmark=None):
+        self.earmark = earmark
         OrderedContainer.__init__(self)
-        self.earmark = Earmark()
 
     def afterAddHook(self, object, container):
         """ See IAddNotifiable. """
+        if self.earmark is None:
+            # XXX need to generate a better earmark that's more likely
+            # to be unique and which you can use to actually identify
+            # the auth service in error messages
+            self.earmark = str(random.randint(0, sys.maxint-1))
 
-        path = getPath(container)
-        self.earmark.addPath(path)
     afterAddHook = ContextMethod(afterAddHook)
 
     def authenticate(self, request):
@@ -91,7 +97,6 @@
     def unauthorized(self, id, request):
         """ See IAuthenticationService. """
 
-        print "in unauthorized", id
         next = queryNextService(self, Authentication, None)
         if next is not None:
             return next.unauthorized(id, request)
@@ -100,18 +105,24 @@
     unauthorized = ContextMethod(unauthorized)
 
     def getPrincipal(self, id):
-        """ See IAuthenticationService. """
+        """ See IAuthenticationService.
+
+        For this implementation, an 'id' is a string which can be
+        split into a 3-tuple by splitting on newline characters.  The
+        three tuple consists of (auth_service_earmark,
+        principal_source_id, principal_id).
 
+        """
         next = None
         
         try:
-            auth_svc_earmark, principal_src_id, principal_id = id
-        except TypeError:
+            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:
             next = queryNextService(self, Authentication, None)
-
         
         if next is not None:
             return next.getPrincipal(id)
@@ -119,15 +130,18 @@
         source = self.get(principal_src_id)
         if source is None:
             raise NotFoundError
-        return source.getPrincipal(principal_id)
+        p = source.getPrincipal(principal_id)
+        return PrincipalWrapper(p, self, id=id)
+
     getPrincipal = ContextMethod(getPrincipal)
 
     def getPrincipals(self, name):
         """ See IAuthenticationService. """
 
-        for ps in self.values():
+        for ps_key, ps in self.items():
             for p in ps.getPrincipals(name):
-                yield p
+                id = '\t'.join((self.earmark, ps_key, str(p.getId())))
+                yield PrincipalWrapper(p, self,id=id)
 
         next = queryNextService(self, Authentication, None)
         if next is not None:
@@ -139,9 +153,9 @@
         """ See IPluggableAuthenticationService.
 
         >>> pas = PluggableAuthenticationService()
-        >>> sps = SimplePrincipalSource()
+        >>> sps = BTreePrincipalSource()
         >>> pas.addPrincipalSource('simple', sps)
-        >>> sps2 = SimplePrincipalSource()
+        >>> sps2 = BTreePrincipalSource()
         >>> pas.addPrincipalSource('not_quite_so_simple', sps2)
         >>> pas.keys()
         ['simple', 'not_quite_so_simple']
@@ -155,11 +169,11 @@
         """ See IPluggableAuthenticationService.
 
         >>> pas = PluggableAuthenticationService()
-        >>> sps = SimplePrincipalSource()
+        >>> sps = BTreePrincipalSource()
         >>> pas.addPrincipalSource('simple', sps)
-        >>> sps2 = SimplePrincipalSource()
+        >>> sps2 = BTreePrincipalSource()
         >>> pas.addPrincipalSource('not_quite_so_simple', sps2)
-        >>> sps3 = SimplePrincipalSource()
+        >>> sps3 = BTreePrincipalSource()
         >>> pas.addPrincipalSource('simpler', sps3)
         >>> pas.keys()
         ['simple', 'not_quite_so_simple', 'simpler']
@@ -173,90 +187,42 @@
 class BTreePrincipalSource(Persistent):
     """An efficient, scalable provider of Authentication Principals."""
 
-    implements(IPrincipalSource, IContainerNamesContainer)
+    implements(ILoginPasswordPrincipalSource, IPrincipalSource,
+               IContainerNamesContainer)
 
     def __init__(self):
 
         self._principals_by_number = IOBTree()
         self._numbers_by_login = OIBTree()
 
-    def getPrincipal(self, id):
-        """ See IReadPrincipalSource.
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin1 = SimplePrincipal(1, 'gandalf', 'shadowfax')
-        >>> sps.setObject('gandalf', prin1)
-        >>> principal = sps.getPrincipal('gandalf')
-        >>> principal.login
-        'gandalf'
-        """
-
-        return self[id]
-
-    def getPrincipals(self, name):
-        """ See IReadPrincipalSource.
-
-        >>> sps = BTreePrincipalSource()
-        >>> prin1 = SimplePrincipal(1, 'gandalf', 'shadowfax')
-        >>> sps.setObject('gandalf', prin1)
-        >>> prin1 = SimplePrincipal(1, 'frodo', 'ring')
-        >>> sps.setObject('frodo', prin1)
-        >>> prin1 = SimplePrincipal(1, 'pippin', 'pipe')
-        >>> sps.setObject('pippin', prin1)
-        >>> prin1 = SimplePrincipal(1, 'sam', 'garden')
-        >>> sps.setObject('sam', prin1)
-        >>> prin1 = SimplePrincipal(1, 'merry', 'food')
-        >>> sps.setObject('merry', 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]
+    # IContainer-related methods
 
-    def __del__(self, key):
+    def __delitem__(self, key):
         """ See IContainer.
 
         >>> sps = BTreePrincipalSource()
-        >>> prin1 = SimplePrincipal(1, 'gandalf', 'shadowfax')
-        >>> sps.setObject('gandalf', prin1)
-        >>> prin1 = SimplePrincipal(1, 'frodo', 'ring')
-        >>> sps.setObject('frodo', prin1)
-        >>> prin1 = SimplePrincipal(1, 'pippin', 'pipe')
-        >>> sps.setObject('pippin', prin1)
-        >>> prin1 = SimplePrincipal(1, 'sam', 'garden')
-        >>> sps.setObject('sam', prin1)
-        >>> prin1 = SimplePrincipal(1, 'merry', 'food')
-        >>> sps.setObject('merry', prin1)
-        >>> [p.login for p in sps.getPrincipals('')]
-        ['frodo', 'gandalf', 'merry', 'pippin', 'sam']
-        >>> sps.removePrincipal('gandalf')
-        >>> [p.login for p in sps.getPrincipals('')]
-        ['frodo', 'merry', 'pippin', 'sam']
-        >>> sps.getPrincipal('gandalf')
-        Traceback (most recent call last):
-        ...
-        KeyError: 'gandalf'
+        >>> prin = SimplePrincipal('fred', 'fred', '123')
+        >>> sps.setObject('fred', prin)
+        'fred'
+        >>> int(sps.get('fred') == prin)
+        1
+        >>> del sps['fred']
+        >>> int(sps.get('fred') == prin)
+        0
+        
         """
-
         number = self._numbers_by_login[key]
 
         del self._principals_by_number[number]
         del self._numbers_by_login[key]
 
     def setObject(self, id, object):
-        """ See IContainer
+        """ See IContainerNamesContainer
 
         >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal(1, 'gandalf', 'shadowfax')
-        >>> sps.setObject('gandalf', prin)
-        >>> sps.getPrincipal('gandalf').login
-        'gandalf'
+        >>> prin = SimplePrincipal('gandalf', 'shadowfax')
+        >>> dummy = sps.setObject('doesntmatter', prin)
+        >>> sps.get('doesntmatter')
         """
 
         store = self._principals_by_number
@@ -276,12 +242,12 @@
         >>> sps = BTreePrincipalSource()
         >>> sps.keys()
         []
-        >>> prin = SimplePrincipal(1, 'arthur', 'tea')
-        >>> key = sps.setObject('arthur', prin)
+        >>> prin = SimplePrincipal('arthur', 'tea')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> sps.keys()
         ['arthur']
-        >>> prin = SimplePrincipal(1, 'ford', 'towel')
-        >>> key = sps.setObject('ford', prin)
+        >>> prin = SimplePrincipal('ford', 'towel')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> sps.keys()
         ['arthur', 'ford']
         """
@@ -294,10 +260,10 @@
         >>> sps = BTreePrincipalSource()
         >>> sps.keys()
         []
-        >>> prin = SimplePrincipal(1, 'trillian', 'heartOfGold')
-        >>> key = sps.setObject('trillian', prin)
-        >>> prin = SimplePrincipal(1, 'zaphod', 'gargleblaster')
-        >>> key = sps.setObject('zaphod', prin)
+        >>> prin = SimplePrincipal('trillian', 'heartOfGold')
+        >>> key = sps.setObject('doesntmatter', prin)
+        >>> prin = SimplePrincipal('zaphod', 'gargleblaster')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> [i for i in sps]
         ['trillian', 'zaphod']
         """
@@ -308,8 +274,8 @@
         """ See IContainer
 
         >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal(1, 'gag', 'justzisguy')
-        >>> key = sps.setObject('gag', prin)
+        >>> prin = SimplePrincipal('gag', 'justzisguy')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> sps['gag'].login
         'gag'
         """
@@ -335,20 +301,18 @@
 
         return self._principals_by_number[number]
 
-    getPrincipalByLogin = get
-
     def values(self):
         """ See IContainer.
 
         >>> sps = BTreePrincipalSource()
         >>> sps.keys()
         []
-        >>> prin = SimplePrincipal(1, 'arthur', 'tea')
-        >>> key = sps.setObject('arthur', prin)
+        >>> prin = SimplePrincipal('arthur', 'tea')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> [user.login for user in sps.values()]
         ['arthur']
-        >>> prin = SimplePrincipal(1, 'ford', 'towel')
-        >>> key = sps.setObject('ford', prin)
+        >>> prin = SimplePrincipal('ford', 'towel')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> [user.login for user in sps.values()]
         ['arthur', 'ford']
         """
@@ -376,12 +340,12 @@
         >>> sps = BTreePrincipalSource()
         >>> sps.keys()
         []
-        >>> prin = SimplePrincipal(1, 'zaphod', 'gargleblaster')
-        >>> key = sps.setObject('zaphod', prin)
+        >>> prin = SimplePrincipal('zaphod', 'gargleblaster')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> [(k, v.login) for k, v in sps.items()]
         [('zaphod', 'zaphod')]
-        >>> prin = SimplePrincipal(1, 'marvin', 'paranoid')
-        >>> key = sps.setObject('marvin', prin)
+        >>> prin = SimplePrincipal('marvin', 'paranoid')
+        >>> key = sps.setObject('doesntmatter', prin)
         >>> [(k, v.login) for k, v in sps.items()]
         [('marvin', 'marvin'), ('zaphod', 'zaphod')]
         """
@@ -393,18 +357,70 @@
         """ See IContainer.
 
         >>> sps = BTreePrincipalSource()
-        >>> prin = SimplePrincipal(1, 'hotblack', 'sundive')
-        >>> key = sps.setObject('hotblack', prin)
-        >>> int('hotblack' in sps)
+        >>> prin = SimplePrincipal('slinkp', 'password')
+        >>> key = sps.setObject('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 IReadPrincipalSource.
+
+        'id' is the id as returned by principal.getId(),
+        not a login.
+
+        """
+        try:
+            id = int(id)
+        except TypeError:
+            raise NotFoundError
+        try:
+            return self._principals_by_number[id]
+        except KeyError:
+            raise NotFoundError
+
+    def getPrincipals(self, name):
+        """ See IReadPrincipalSource.
+
+        >>> sps = BTreePrincipalSource()
+        >>> prin1 = SimplePrincipal('gandalf', 'shadowfax')
+        >>> dummy = sps.setObject('doesntmatter', prin1)
+        >>> prin1 = SimplePrincipal('frodo', 'ring')
+        >>> dummy = sps.setObject('doesntmatter', prin1)
+        >>> prin1 = SimplePrincipal('pippin', 'pipe')
+        >>> dummy = sps.setObject('doesntmatter', prin1)
+        >>> prin1 = SimplePrincipal('sam', 'garden')
+        >>> dummy = sps.setObject('doesntmatter', prin1)
+        >>> prin1 = SimplePrincipal('merry', 'food')
+        >>> dummy = sps.setObject('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.validate(password):
+            return user
+
 class SimplePrincipal(Persistent):
     """A no-frills IUserSchemafied implementation."""
 
@@ -419,18 +435,25 @@
         self.description = description
 
     def getId(self):
+        """ See IPrincipal. """
         return self.id
 
-    def getLogin(self):
-        return self.login
-    
     def getTitle(self):
+        """ See IPrincipal. """
         return self.title
 
+    def getDescription(self):
+        """ See IPrincipal. """
+        return self.description
+
+    def getLogin(self):
+        """ See IReadUser. """
+        return self.login
+    
     def validate(self, test_password):
         """ See IReadUser.
 
-        >>> pal = SimplePrincipal(1, 'gandalf', 'shadowfax', 'The Grey Wizard',
+        >>> pal = SimplePrincipal('gandalf', 'shadowfax', 'The Grey Wizard',
         ...                       'Cool old man with neato fireworks. '
         ...                       'Has a nice beard.')
         >>> int(pal.validate('shdaowfax'))
@@ -441,36 +464,6 @@
 
         return test_password == self.password
 
-class Earmark(Persistent):
-    """A distinguishing feature by which an object can be uniquely id'd."""
-
-    def __init__(self):
-
-        self.paths = []
-        self.times = []
-        self.original_time = datetime.datetime.utcnow()
-
-    # XXX we may want to bring in additional descriptive metadata eventually
-
-    def addPath(self, path):
-        """ Update the history of places this Earmark has been.
-
-
-        >>> em = Earmark()
-        >>> em.addPath('/was/here')
-        >>> em.addPath('/am/now/here')
-        >>> em.paths
-        ['/was/here', '/am/now/here']
-        """
-        
-        self.paths.append(path)
-        self.times.append(datetime.datetime.utcnow())
-
-    def __hash__(self):
-        # use the persistent object id as the hash as opposed to
-        # using the memory address as a hash.
-        return hash(self._p_oid)
-
 class PrincipalAuthenticationView:
     implements(IViewFactory)
     
@@ -484,20 +477,19 @@
             if a is None:
                 return
             login = a.getLogin()
-            if login is None:
-                return
-
-            p = self.context.getPrincipalByLogin(login)
-            if p is None:
-                return
             password = a.getPassword()
 
-            if p.validate(password):
-                # XXX we may in the future get back something from getPrincipal
-                # that is not a user; we likely won't be able to call
-                # 'validate' on this object.
-                return p
+            p = self.context.authenticate(login, password)
+            return p
+
+    
+class PrincipalWrapper(Wrapper):
+    """ A wrapper for a principal as returned from the authentication
+    service.  The id of the principal as returned by the wrapper is
+    a three-tuple instead of the integer id returned by the simple
+    principal."""
+    def getId(self):
+        """ Return the id as passed in to the wrapper """
+        return getWrapperData(self)['id']
 
-    def unauthorized(self):
-        return 'Unauthorized'