[Zope3-checkins] CVS: Zope3/src/zope/app/services/pluggableauth - __init__.py:1.1.2.1 configure.zcml:1.1.2.1
Zachery Bir
zbir@urbanape.com
Tue, 3 Jun 2003 09:34:07 -0400
Update of /cvs-repository/Zope3/src/zope/app/services/pluggableauth
In directory cvs.zope.org:/tmp/cvs-serv21295/src/zope/app/services/pluggableauth
Added Files:
Tag: pluggable_authentication_service-branch
__init__.py configure.zcml
Log Message:
In directory src/zope/app/browser/services/pluggableauth:
- src/zope/app/browser/services/pluggableauth/__init__.py
Adding override for the PluggableAuthenticationService
- src/zope/app/browser/services/pluggableauth/configure.zcml
Configuration for the views for PluggableAuthenticationService,
BTreePrincipalSource, and SimplePrincipal
XXX: TODO: - contents.html view for PluggableAuthenticationService
and BTreePrincipalSource
- get BTreePrincipalSource to actually have additional
zmi_views to be more like a container, for example, it
currently does not have a "Contents" tab
- views for the login challenge
In directory src/zope/app/container:
- src/zope/app/container/ordered.py
Implementation of an OrderedContainer
In directory src/zope/app/container/tests:
- src/zope/app/container/tests/test_ordered.py
Test module for OrderedContainer (all tests currently in docstrings)
In directory src/zope/app/interfaces/container:
- src/zope/app/interfaces/container/__init__.py
Added interface for OrderedContainer
In directory src/zope/app/interfaces/services/pluggableauth:
- src/zope/app/interfaces/services/pluggableauth/__init__.py
Interfaces for PluggableAuthenticationService, Read/WritePrincipalSource,
and UserSchemafied
In directory src/zope/app/services:
- src/zope/app/services/configure.zcml
Included the pluggableauth package
In directory src/zope/app/services/pluggableauth:
- src/zope/app/services/pluggableauth/__init__.py
Implementation of the PluggableAuthenticationService, BTreePrincipalSource,
and SimplePrincipal classes
XXX: TODO: - Wrap all returned items from getPrincipals() in both
PluggableAuthenticationService and BTreePrincipalSource
so that the ids being handed up are tuplified (in the case
of BTreePrincipalSource) and triplified (in the case of
PluggableAuthenticationService) to ensure proper uniquity
- src/zope/app/services/pluggableauth/configure.zcml
Content directives for the above
In directory src/zope/app/services/tests:
- src/zope/app/services/tests/test_pluggableauth.py
Test module for PluggableAuthenticationService, BTreePrincipalSource, and
SimplePrincipal classes (all available tests currently in docstrings)
XXX: TODO: - write unit tests for the ContextMethods to ensure proper
delegation of Authentication responsibility (perhaps
functional tests, instead?)
=== Added File Zope3/src/zope/app/services/pluggableauth/__init__.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Pluggable Authentication service implementation.
$Id: __init__.py,v 1.1.2.1 2003/06/03 13:34:06 urbanape Exp $
"""
import random
import sys
import datetime
from persistence import Persistent
from zodb.btrees.IOBTree import IOBTree
from zodb.btrees.OIBTree import OIBTree
from zope.interface import implements
from zope.app.services.servicenames import Authentication
from zope.app.container.btree import BTreeContainer
from zope.app.container.ordered import OrderedContainer
from zope.app.interfaces.container import IContainer
from zope.app.interfaces.container import IContainerNamesContainer
from zope.app.interfaces.container import IOrderedContainer
from zope.app.interfaces.container import IAddNotifiable
from zope.app.interfaces.services.pluggableauth import IUserSchemafied
from zope.app.interfaces.security import ILoginPassword
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.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
def gen_key():
"""Return a random int (1, MAXINT), suitable for use as a BTree key."""
return random.randint(0,sys.maxint)
class PluggableAuthenticationService(OrderedContainer):
implements(IPluggableAuthenticationService, ISimpleService,
IOrderedContainer, IAddNotifiable)
def __init__(self):
OrderedContainer.__init__(self)
self.earmark = Earmark()
def afterAddHook(self, object, container):
""" See IAddNotifiable. """
path = getPath(container)
self.earmark.addPath(path)
afterAddHook = ContextMethod(afterAddHook)
def authenticate(self, request):
""" See IAuthenticationService. """
for ps in self.values():
loginView = 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
authenticate = ContextMethod(authenticate)
def unauthenticatedPrincipal(self):
""" See IAuthenticationService. """
return None # XXX Do we need to implement or use another?
def unauthorized(self, id, request):
""" See IAuthenticationService. """
if len(self) > 0:
loginView = queryView(self[0], "login", request)
if loginView is not None:
return loginView.unauthorized()
next = queryNextService(self, Authentication, None)
if next is not None:
return next.unauthorized(request)
return None
unauthorized = ContextMethod(unauthorized)
def getPrincipal(self, id):
""" See IAuthenticationService. """
next = None
try:
auth_svc_earmark, principal_src, principal_id = id
except ValueError:
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)
source = self[principal_src]
return source.getPrincipal(principal_id)
getPrincipal = ContextMethod(getPrincipal)
def getPrincipals(self, name):
""" See IAuthenticationService. """
for ps in self.values():
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
getPrincipals = ContextMethod(getPrincipals)
def addPrincipalSource(self, id, principal_source):
""" See IPluggableAuthenticationService.
>>> pas = PluggableAuthenticationService()
>>> sps = SimplePrincipalSource()
>>> pas.addPrincipalSource('simple', sps)
>>> sps2 = SimplePrincipalSource()
>>> pas.addPrincipalSource('not_quite_so_simple', sps2)
>>> pas.keys()
['simple', 'not_quite_so_simple']
"""
if not IReadPrincipalSource.isImplementedBy(principal_source):
raise TypeError("Source must implement IReadPrincipalSource")
self.setObject(id, principal_source)
def removePrincipalSource(self, id):
""" See IPluggableAuthenticationService.
>>> pas = PluggableAuthenticationService()
>>> sps = SimplePrincipalSource()
>>> pas.addPrincipalSource('simple', sps)
>>> sps2 = SimplePrincipalSource()
>>> pas.addPrincipalSource('not_quite_so_simple', sps2)
>>> sps3 = SimplePrincipalSource()
>>> 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]
class BTreePrincipalSource(Persistent):
"""An efficient, scalable provider of Authentication Principals."""
implements(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]
def __del__(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'
"""
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
>>> sps = BTreePrincipalSource()
>>> prin = SimplePrincipal(1, 'gandalf', 'shadowfax')
>>> sps.setObject('gandalf', prin)
>>> sps.getPrincipal('gandalf').login
'gandalf'
"""
store = self._principals_by_number
key = gen_key()
while not store.insert(key, object):
key = gen_key()
object.id = key
self._numbers_by_login[object.login] = key
return object.login
def keys(self):
""" See IContainer.
>>> sps = BTreePrincipalSource()
>>> sps.keys()
[]
>>> prin = SimplePrincipal(1, 'arthur', 'tea')
>>> key = sps.setObject('arthur', prin)
>>> sps.keys()
['arthur']
>>> prin = SimplePrincipal(1, 'ford', 'towel')
>>> key = sps.setObject('ford', prin)
>>> sps.keys()
['arthur', 'ford']
"""
return list(self._numbers_by_login.keys())
def __iter__(self):
""" See IContainer.
>>> sps = BTreePrincipalSource()
>>> sps.keys()
[]
>>> prin = SimplePrincipal(1, 'trillian', 'heartOfGold')
>>> key = sps.setObject('trillian', prin)
>>> prin = SimplePrincipal(1, 'zaphod', 'gargleblaster')
>>> key = sps.setObject('zaphod', prin)
>>> [i for i in sps]
['trillian', 'zaphod']
"""
return iter(self.keys())
def __getitem__(self, key):
""" See IContainer
>>> sps = BTreePrincipalSource()
>>> prin = SimplePrincipal(1, 'gag', 'justzisguy')
>>> key = sps.setObject('gag', 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')
>>> key = sps.setObject('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(1, 'arthur', 'tea')
>>> key = sps.setObject('arthur', prin)
>>> [user.login for user in sps.values()]
['arthur']
>>> prin = SimplePrincipal(1, 'ford', 'towel')
>>> key = sps.setObject('ford', 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')
>>> key = sps.setObject('trillian', prin)
>>> int(len(sps) == 1)
1
"""
return len(self._principals_by_number)
def items(self):
""" See IContainer.
>>> sps = BTreePrincipalSource()
>>> sps.keys()
[]
>>> prin = SimplePrincipal(1, 'zaphod', 'gargleblaster')
>>> key = sps.setObject('zaphod', prin)
>>> [(k, v.login) for k, v in sps.items()]
[('zaphod', 'zaphod')]
>>> prin = SimplePrincipal(1, 'marvin', 'paranoid')
>>> key = sps.setObject('marvin', 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(1, 'hotblack', 'sundive')
>>> key = sps.setObject('hotblack', prin)
>>> int('hotblack' in sps)
1
>>> int('desiato' in sps)
0
"""
return self._numbers_by_login.has_key(key)
has_key = __contains__
class SimplePrincipal(Persistent):
"""A no-frills IUserSchemafied implementation."""
implements(IUserSchemafied)
def __init__(self, login, password, title='', description=''):
self.id = ''
self.login = login
self.password = password
self.title = title
self.description = description
def validate(self, test_password):
""" See IReadUser.
>>> pal = SimplePrincipal(1, 'gandalf', 'shadowfax', 'The Grey Wizard',
... 'Cool old man with neato fireworks. '
... 'Has a nice beard.')
>>> int(pal.validate('shdaowfax'))
0
>>> int(pal.validate('shadowfax'))
1
"""
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())
=== Added File Zope3/src/zope/app/services/pluggableauth/configure.zcml ===
<zopeConfigure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<content class=".PluggableAuthenticationService">
<factory
id="zope.app.services.PluggableAuthenticationService"
permission="zope.ManageServices"
/>
<require
permission="zope.ManageServices"
interface="
zope.app.interfaces.services.pluggableauth.IPluggableAuthenticationService"
/>
<allow
interface="zope.app.interfaces.container.IReadContainer"
/>
<require
permission="zope.ManageServices"
interface="zope.app.interfaces.container.IWriteContainer"
/>
<require
permission="zope.ManageServices"
interface="zope.app.interfaces.container.IAddNotifiable"
/>
<require
permission="zope.ManageServices"
interface="zope.app.interfaces.services.service.ISimpleService"
/>
</content>
<content class=".BTreePrincipalSource">
<factory
id="zope.app.principalsources.BTreePrincipalSource"
permission="zope.ManageServices"
/>
<allow
interface="zope.app.interfaces.container.IReadContainer"
/>
<require
permission="zope.ManageServices"
interface="zope.app.interfaces.container.IWriteContainer"
/>
<require
permission="zope.ManageServices"
interface="
zope.app.interfaces.services.pluggableauth.IWritePrincipalSource"
/>
<allow
interface="
zope.app.interfaces.services.pluggableauth.IReadPrincipalSource"
/>
</content>
<content class=".SimplePrincipal">
<factory
id="zope.app.principals.SimplePrincipal"
permission="zope.ManageServices"
/>
<allow
interface="zope.app.interfaces.services.pluggableauth.IUserSchemafied"
/>
<require
permission="zope.ManageServices"
set_schema="zope.app.interfaces.services.pluggableauth.IUserSchemafied"
/>
</content>
</zopeConfigure>