[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'