[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/authentication/
Added support for unauthenticated principals.
Jim Fulton
jim at zope.com
Thu Feb 3 18:22:37 EST 2005
Log message for revision 29031:
Added support for unauthenticated principals.
Changed:
U Zope3/trunk/src/zope/app/authentication/README.txt
U Zope3/trunk/src/zope/app/authentication/authentication.py
U Zope3/trunk/src/zope/app/authentication/interfaces.py
-=-
Modified: Zope3/trunk/src/zope/app/authentication/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/authentication/README.txt 2005-02-03 23:06:30 UTC (rev 29030)
+++ Zope3/trunk/src/zope/app/authentication/README.txt 2005-02-03 23:22:37 UTC (rev 29031)
@@ -42,34 +42,32 @@
Let's look at an example. We create a simple plugin that provides credential
extraction:
- >>> import zope.interface
+ >>> from zope import interface, component
>>> from zope.app.authentication import interfaces
>>> class MyExtractor:
...
- ... zope.interface.implements(interfaces.IExtractionPlugin)
+ ... interface.implements(interfaces.IExtractionPlugin)
...
... def extractCredentials(self, request):
... return request.get('credentials')
We need to register this as a utility. Normally, we'd do this in ZCML. For the
-example here, we'll use the `provideUtility()` function from
-`zope.app.tests.ztapi`:
+example here, we'll use the `provideUtility()`:
- >>> from zope.app.tests.ztapi import provideUtility
- >>> provideUtility(interfaces.IExtractionPlugin, MyExtractor(), name='emy')
+ >>> component.provideUtility(MyExtractor(), name='emy')
Now we also create an authenticator plugin that knows about object 42:
>>> class Auth42:
...
- ... zope.interface.implements(interfaces.IAuthenticationPlugin)
+ ... interface.implements(interfaces.IAuthenticationPlugin)
...
... def authenticateCredentials(self, credentials):
... if credentials == 42:
... return '42', {'domain': 42}
- >>> provideUtility(interfaces.IAuthenticationPlugin, Auth42(), name='a42')
+ >>> component.provideUtility(Auth42(), name='a42')
We provide a principal factory plugin:
@@ -81,12 +79,13 @@
... self.id = id
...
... def __repr__(self):
- ... return 'Principal(%r, %r)' % (self.id, self.title)
+ ... return '%s(%r, %r)' % (self.__class__.__name__,
+ ... self.id, self.title)
>>> from zope.event import notify
>>> class PrincipalFactory:
...
- ... zope.interface.implements(interfaces.IPrincipalFactoryPlugin)
+ ... interface.implements(interfaces.IPrincipalFactoryPlugin)
...
... def createAuthenticatedPrincipal(self, id, info, request):
... principal = Principal(id)
@@ -99,8 +98,7 @@
... notify(interfaces.FoundPrincipalCreated(principal, info))
... return principal
- >>> provideUtility(interfaces.IPrincipalFactoryPlugin, PrincipalFactory(),
- ... name='pf')
+ >>> component.provideUtility(PrincipalFactory(), name='pf')
Finally, we create a pluggable-authentication utility instance:
@@ -166,13 +164,13 @@
>>> class AuthInt:
...
- ... zope.interface.implements(interfaces.IAuthenticationPlugin)
+ ... interface.implements(interfaces.IAuthenticationPlugin)
...
... def authenticateCredentials(self, credentials):
... if isinstance(credentials, int):
... return str(credentials), {'int': credentials}
- >>> provideUtility(interfaces.IAuthenticationPlugin, AuthInt(), name='aint')
+ >>> component.provideUtility(AuthInt(), name='aint')
If we put it before the original authenticator:
@@ -199,14 +197,14 @@
>>> class OddExtractor:
...
- ... zope.interface.implements(interfaces.IExtractionPlugin)
+ ... interface.implements(interfaces.IExtractionPlugin)
...
... def extractCredentials(self, request):
... credentials = request.get('credentials')
... if isinstance(credentials, int) and (credentials%2):
... return 1
- >>> provideUtility(interfaces.IExtractionPlugin, OddExtractor(), name='eodd')
+ >>> component.provideUtility(OddExtractor(), name='eodd')
>>> auth.extractors = 'eodd', 'emy'
>>> request = TestRequest(credentials=41)
@@ -226,7 +224,7 @@
>>> class OddFactory:
...
- ... zope.interface.implements(interfaces.IPrincipalFactoryPlugin)
+ ... interface.implements(interfaces.IPrincipalFactoryPlugin)
...
... def createAuthenticatedPrincipal(self, id, info, request):
... i = info.get('int')
@@ -246,8 +244,7 @@
... principal, info))
... return principal
- >>> provideUtility(interfaces.IPrincipalFactoryPlugin, OddFactory(),
- ... name='oddf')
+ >>> component.provideUtility(OddFactory(), name='oddf')
>>> auth.factories = 'oddf', 'pf'
@@ -277,18 +274,17 @@
>>> class Search42:
...
- ... zope.interface.implements(interfaces.IPrincipalSearchPlugin)
+ ... interface.implements(interfaces.IPrincipalSearchPlugin)
...
... def principalInfo(self, principal_id):
... if principal_id == '42':
... return {'domain': 42}
- >>> provideUtility(interfaces.IPrincipalSearchPlugin, Search42(),
- ... name='s42')
+ >>> component.provideUtility(Search42(), name='s42')
>>> class IntSearch:
...
- ... zope.interface.implements(interfaces.IPrincipalSearchPlugin)
+ ... interface.implements(interfaces.IPrincipalSearchPlugin)
...
... def principalInfo(self, principal_id):
... try:
@@ -298,8 +294,7 @@
... if (i >= 0 and i < 100):
... return {'int': i}
- >>> provideUtility(interfaces.IPrincipalSearchPlugin, IntSearch(),
- ... name='sint')
+ >>> component.provideUtility(IntSearch(), name='sint')
>>> auth.searchers = 's42', 'sint'
@@ -328,7 +323,7 @@
>>> class FakeAuthUtility:
...
- ... zope.interface.implements(IAuthentication)
+ ... interface.implements(IAuthentication)
...
... lastGetPrincipalCall = lastUnauthorizedCall = None
...
@@ -368,13 +363,13 @@
>>> class Challenge:
...
- ... zope.interface.implements(interfaces.IChallengePlugin)
+ ... interface.implements(interfaces.IChallengePlugin)
...
... def challenge(self, requests, response):
... response.setHeader('X-Unauthorized', 'True')
... return True
- >>> provideUtility(interfaces.IChallengePlugin, Challenge(), name='c')
+ >>> component.provideUtility(Challenge(), name='c')
>>> auth.challengers = ('c', )
Now if we call unauthorized:
@@ -422,7 +417,7 @@
add challenges to a X-Challenges headers:
>>> class ColorChallenge:
- ... zope.interface.implements(interfaces.IChallengePlugin)
+ ... interface.implements(interfaces.IChallengePlugin)
...
... protocol = 'bridge'
...
@@ -432,11 +427,11 @@
... challenge + 'favorite color? ')
... return True
- >>> provideUtility(interfaces.IChallengePlugin, ColorChallenge(), name='cc')
+ >>> component.provideUtility(ColorChallenge(), name='cc')
>>> auth.challengers = 'cc, ', 'c'
>>> class BirdChallenge:
- ... zope.interface.implements(interfaces.IChallengePlugin)
+ ... interface.implements(interfaces.IChallengePlugin)
...
... protocol = 'bridge'
...
@@ -446,7 +441,7 @@
... challenge + 'swallow air speed? ')
... return True
- >>> provideUtility(interfaces.IChallengePlugin, BirdChallenge(), name='bc')
+ >>> component.provideUtility(BirdChallenge(), name='bc')
>>> auth.challengers = 'cc', 'c', 'bc'
Now if we call unauthorized:
@@ -550,6 +545,43 @@
... for (id, queriable) in auth.getQueriables()]
['Search42', 'IntSearch']
+Unauthenticated principals
+==========================
+
+Normally, the pluggable-authentication utility returns None when asked
+for an unauthenticated principal:
+
+ >>> auth.unauthenticatedPrincipal()
+
+However, if an IUnauthenticatedPrincipalFactoryPlugin utility is
+defined. then it will be used to create an IUnauthenticatedPrincipal:
+
+ >>> import zope.app.security.interfaces
+ >>> class UnauthenticatedPrincipal(Principal):
+ ... interface.implements(
+ ... zope.app.security.interfaces.IUnauthenticatedPrincipal)
+
+ >>> class UnauthenticatedPrincipalFactoryPlugin:
+ ... interface.implements(
+ ... interfaces.IUnauthenticatedPrincipalFactoryPlugin)
+ ...
+ ... def createUnauthenticatedPrincipal(self):
+ ... principal = UnauthenticatedPrincipal('u')
+ ... notify(interfaces.UnauthenticatedPrincipalCreated(principal))
+ ... return principal
+
+ >>> component.provideUtility(UnauthenticatedPrincipalFactoryPlugin())
+
+ >>> clearEvents()
+ >>> prin = auth.unauthenticatedPrincipal()
+ >>> prin
+ UnauthenticatedPrincipal('u', '{}')
+
+ >>> [event] = getEvents(interfaces.IUnauthenticatedPrincipalCreated)
+ >>> event.principal is prin
+ True
+
+
Design Notes
============
Modified: Zope3/trunk/src/zope/app/authentication/authentication.py
===================================================================
--- Zope3/trunk/src/zope/app/authentication/authentication.py 2005-02-03 23:06:30 UTC (rev 29030)
+++ Zope3/trunk/src/zope/app/authentication/authentication.py 2005-02-03 23:22:37 UTC (rev 29031)
@@ -23,7 +23,7 @@
from zope.schema.interfaces import ISourceQueriables
-from zope.app import zapi
+from zope import component
from zope.app.security.interfaces import IAuthentication
from zope.app.utility.utility import queryNextUtility
@@ -35,6 +35,8 @@
from zope.app.authentication.interfaces import IAuthenticationPlugin
from zope.app.authentication.interfaces import IChallengePlugin
from zope.app.authentication.interfaces import IPrincipalFactoryPlugin
+from zope.app.authentication.interfaces \
+ import IUnauthenticatedPrincipalFactoryPlugin
from zope.app.authentication.interfaces import IPrincipalSearchPlugin
from zope.app.authentication.interfaces import IPluggableAuthentication
@@ -49,10 +51,10 @@
self.prefix = prefix
def authenticate(self, request):
- authenticators = [zapi.queryUtility(IAuthenticationPlugin, name)
+ authenticators = [component.queryUtility(IAuthenticationPlugin, name)
for name in self.authenticators]
for extractor in self.extractors:
- extractor = zapi.queryUtility(IExtractionPlugin, extractor)
+ extractor = component.queryUtility(IExtractionPlugin, extractor)
if extractor is None:
continue
credentials = extractor.extractCredentials(request)
@@ -72,8 +74,8 @@
def _create(self, meth, *args):
# We got some data, lets create a user
for factory in self.factories:
- factory = zapi.queryUtility(IPrincipalFactoryPlugin,
- factory)
+ factory = component.queryUtility(IPrincipalFactoryPlugin,
+ factory)
if factory is None:
continue
@@ -89,7 +91,7 @@
id = id[len(self.prefix):]
for searcher in self.searchers:
- searcher = zapi.queryUtility(IPrincipalSearchPlugin, searcher)
+ searcher = component.queryUtility(IPrincipalSearchPlugin, searcher)
if searcher is None:
continue
@@ -103,18 +105,23 @@
def getQueriables(self):
for searcher_id in self.searchers:
- searcher = zapi.queryUtility(IPrincipalSearchPlugin, searcher_id)
+ searcher = component.queryUtility(IPrincipalSearchPlugin,
+ searcher_id)
yield searcher_id, searcher
def unauthenticatedPrincipal(self):
+ factory = component.queryUtility(
+ IUnauthenticatedPrincipalFactoryPlugin)
+ if factory is not None:
+ return factory.createUnauthenticatedPrincipal()
return None
def unauthorized(self, id, request):
protocol = None
for challenger in self.challengers:
- challenger = zapi.queryUtility(IChallengePlugin, challenger)
+ challenger = component.queryUtility(IChallengePlugin, challenger)
if challenger is None:
continue # skip non-existant challengers
Modified: Zope3/trunk/src/zope/app/authentication/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/authentication/interfaces.py 2005-02-03 23:06:30 UTC (rev 29030)
+++ Zope3/trunk/src/zope/app/authentication/interfaces.py 2005-02-03 23:22:37 UTC (rev 29031)
@@ -92,6 +92,21 @@
self.info = info
self.request = request
+class IUnauthenticatedPrincipalCreated(IPrincipalCreated):
+ """An authenticated principal object has been created
+
+ This event is generated when a principal has been created by
+ authenticating a request.
+ """
+
+class UnauthenticatedPrincipalCreated:
+
+ zope.interface.implements(IUnauthenticatedPrincipalCreated)
+
+ def __init__(self, principal):
+ self.principal = principal
+ self.info = {}
+
class IFoundPrincipalCreated(IPrincipalCreated):
"""Event indicating that a principal was created based on a search
"""
@@ -193,6 +208,14 @@
principal is created, return None.
"""
+class IUnauthenticatedPrincipalFactoryPlugin(IPlugin):
+ """Create an unauthenticated principal
+ """
+
+ def createUnauthenticatedPrincipal():
+ """Create an unauthenticated principal
+ """
+
class IPrincipalSearchPlugin(IPrincipalIdAwarePlugin):
"""Find principals.
More information about the Zope3-Checkins
mailing list