[Zope3-checkins] CVS: Zope3/src/zope/app/services/pluggableauth - __init__.py:1.3
Chris McDonough
chrism@zope.com
Mon, 23 Jun 2003 22:35:00 -0400
Update of /cvs-repository/Zope3/src/zope/app/services/pluggableauth
In directory cvs.zope.org:/tmp/cvs-serv30249/services/pluggableauth
Modified Files:
__init__.py
Log Message:
- Change earmark generation to really create a unique earmark.
- Add more comments.
- Raise more descriptive error messages.
=== Zope3/src/zope/app/services/pluggableauth/__init__.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/services/pluggableauth/__init__.py:1.2 Mon Jun 23 18:46:17 2003
+++ Zope3/src/zope/app/services/pluggableauth/__init__.py Mon Jun 23 22:34:29 2003
@@ -20,6 +20,7 @@
import random
import sys
import datetime
+import time
from persistence import Persistent
from zodb.btrees.IOBTree import IOBTree
from zodb.btrees.OIBTree import OIBTree
@@ -44,7 +45,7 @@
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 import zapi
from zope.app.traversing import getPath
from zope.context import ContextMethod
from zope.exceptions import NotFoundError
@@ -62,22 +63,33 @@
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 afterAddHook(self, object, container):
+ def afterAddHook(self, ob, 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))
+ # we manufacture what is intended to be a globally unique
+ # earmark if one is not provided in __init__
+ myname = zapi.name(ob)
+ rand_id = gen_key()
+ t = int(time.time())
+ self.earmark = '%s-%s-%s' % (myname, rand_id, t)
afterAddHook = ContextMethod(afterAddHook)
def authenticate(self, request):
""" See IAuthenticationService. """
for ps_key, ps in self.items():
- loginView = queryView(ps, "login", request)
+ loginView = zapi.queryView(ps, "login", request)
if loginView is not None:
principal = loginView.authenticate()
if principal is not None:
@@ -110,10 +122,15 @@
""" See IAuthenticationService.
For this implementation, an 'id' is a string which can be
- split into a 3-tuple by splitting on newline characters. The
+ 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
@@ -124,6 +141,7 @@
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:
@@ -131,7 +149,7 @@
source = self.get(principal_src_id)
if source is None:
- raise NotFoundError
+ raise NotFoundError, principal_src_id
p = source.getPrincipal(principal_id)
return PrincipalWrapper(p, self, id=id)
@@ -218,7 +236,7 @@
del self._principals_by_number[number]
del self._numbers_by_login[key]
- def setObject(self, id, object):
+ def setObject(self, id, ob):
""" See IContainerNamesContainer
>>> sps = BTreePrincipalSource()
@@ -230,13 +248,13 @@
store = self._principals_by_number
key = gen_key()
- while not store.insert(key, object):
+ while not store.insert(key, ob):
key = gen_key()
- object.id = key
- self._numbers_by_login[object.login] = key
+ ob.id = key
+ self._numbers_by_login[ob.login] = key
- return object.login
+ return ob.login
def keys(self):
""" See IContainer.
@@ -382,11 +400,11 @@
try:
id = int(id)
except TypeError:
- raise NotFoundError
+ raise NotFoundError, id
try:
return self._principals_by_number[id]
except KeyError:
- raise NotFoundError
+ raise NotFoundError, id
def getPrincipals(self, name):
""" See IReadPrincipalSource.
@@ -420,7 +438,7 @@
if number is None:
return
user = self._principals_by_number[number]
- if user.validate(password):
+ if user.password == password:
return user
class SimplePrincipal(Persistent):
@@ -474,22 +492,26 @@
self.request = request
def authenticate(self):
- for interface in (ILoginPassword,):
- a = queryAdapter(self.request, interface, None)
- if a is None:
- return
- login = a.getLogin()
- password = a.getPassword()
+ # XXX we only handle 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.
+ a = queryAdapter(self.request, ILoginPassword, None)
+ if a is None:
+ return
+ login = a.getLogin()
+ password = a.getPassword()
- p = self.context.authenticate(login, password)
- 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."""
+ service. This wrapper returns the principal id which includes
+ identification of the auth service and the principal store id in
+ addition to the principal id."""
+
def getId(self):
""" Return the id as passed in to the wrapper """
return getWrapperData(self)['id']