[Checkins] SVN: zope.app.authentication/trunk/src/zope/app/authentication/ Fixed a bug with authenticator plugins providing ILocation without being actually located anywhere.

Christian Zagrodnick cz at gocept.com
Mon Jun 25 10:32:13 EDT 2007


Log message for revision 77044:
  Fixed a bug with authenticator plugins providing ILocation without being actually located anywhere.

Changed:
  U   zope.app.authentication/trunk/src/zope/app/authentication/README.txt
  U   zope.app.authentication/trunk/src/zope/app/authentication/authentication.py

-=-
Modified: zope.app.authentication/trunk/src/zope/app/authentication/README.txt
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/README.txt	2007-06-25 14:22:46 UTC (rev 77043)
+++ zope.app.authentication/trunk/src/zope/app/authentication/README.txt	2007-06-25 14:32:12 UTC (rev 77044)
@@ -45,7 +45,7 @@
 Simple Credentials Plugin
 -------------------------
 
-To illustrate, we'll create a simple credentials plugin:
+To illustrate, we'll create a simple credentials plugin::
 
   >>> from zope import interface
   >>> from zope.app.authentication import interfaces
@@ -63,7 +63,7 @@
   ...     def logout(self, request):
   ...         pass # logout is a no-op for this plugin
 
-As a plugin, MyCredentialsPlugin needs to be registered as a named utility:
+As a plugin, MyCredentialsPlugin needs to be registered as a named utility::
 
   >>> myCredentialsPlugin = MyCredentialsPlugin()
   >>> provideUtility(myCredentialsPlugin, name='My Credentials Plugin')
@@ -72,7 +72,7 @@
 ---------------------------
 
 Next we'll create a simple authenticator plugin. For our plugin, we'll need
-an implementation of IPrincipalInfo:
+an implementation of IPrincipalInfo::
 
   >>> class PrincipalInfo(object):
   ...
@@ -86,7 +86,7 @@
   ...     def __repr__(self):
   ...         return 'PrincipalInfo(%r)' % self.id
 
-Our authenticator uses this type when it creates a principal info:
+Our authenticator uses this type when it creates a principal info::
 
   >>> class MyAuthenticatorPlugin(object):
   ...
@@ -100,7 +100,7 @@
   ...         pass # plugin not currently supporting search
 
 As with the credentials plugin, the authenticator plugin must be registered
-as a named utility:
+as a named utility::
 
   >>> myAuthenticatorPlugin = MyAuthenticatorPlugin()
   >>> provideUtility(myAuthenticatorPlugin, name='My Authenticator Plugin')
@@ -109,7 +109,7 @@
 -------------------
 While authenticator plugins provide principal info, they are not responsible
 for creating principals. This function is performed by factory adapters. For
-these tests we'll borrow some factories from the principal folder:
+these tests we'll borrow some factories from the principal folder::
 
   >>> from zope.app.authentication import principalfolder
   >>> provideAdapter(principalfolder.AuthenticatedPrincipalFactory)
@@ -120,12 +120,12 @@
 Configuring a PAU
 -----------------
 
-Finally, we'll create the PAU itself:
+Finally, we'll create the PAU itself::
 
   >>> from zope.app import authentication
   >>> pau = authentication.PluggableAuthentication('xyz_')
 
-and configure it with the two plugins:
+and configure it with the two plugins::
 
   >>> pau.credentialsPlugins = ('My Credentials Plugin', )
   >>> pau.authenticatorPlugins = ('My Authenticator Plugin', )
@@ -133,19 +133,19 @@
 Using the PAU to Authenticate
 -----------------------------
 
-We can now use the PAU to authenticate a sample request:
+We can now use the PAU to authenticate a sample request::
 
   >>> from zope.publisher.browser import TestRequest
   >>> print pau.authenticate(TestRequest())
   None
 
 In this case, we cannot authenticate an empty request. In the same way, we
-will not be able to authenticate a request with the wrong credentials:
+will not be able to authenticate a request with the wrong credentials::
 
   >>> print pau.authenticate(TestRequest(credentials='let me in!'))
   None
 
-However, if we provide the proper credentials:
+However, if we provide the proper credentials::
 
   >>> request = TestRequest(credentials='secretcode')
   >>> principal = pau.authenticate(request)
@@ -157,7 +157,7 @@
 Authenticated Principal Creates Events
 --------------------------------------
 
-We can verify that the appropriate event was published:
+We can verify that the appropriate event was published::
 
   >>> [event] = getEvents(interfaces.IAuthenticatedPrincipalCreated)
   >>> event.principal is principal
@@ -169,7 +169,7 @@
 
 The info object has the id, title, and description of the principal.  The info
 object is also generated by the authenticator plugin, so the plugin may
-itself have provided additional information on the info object.
+itself have provided additional information on the info object::
 
   >>> event.info.title
   'Bob'
@@ -183,7 +183,7 @@
 authenticate this principal.  These attributes can be useful for subscribers
 that want to react to the plugins used.  For instance, subscribers can
 determine that a given credential plugin does or does not support logout, and
-provide information usable to show or hide logout user interface.
+provide information usable to show or hide logout user interface::
 
   >>> event.info.credentialsPlugin is myCredentialsPlugin
   True
@@ -192,13 +192,13 @@
 
 Normally, we provide subscribers to these events that add additional
 information to the principal. For example, we'll add one that sets
-the title:
+the title::
 
   >>> def add_info(event):
   ...     event.principal.title = event.info.title
   >>> subscribe([interfaces.IAuthenticatedPrincipalCreated], None, add_info)
 
-Now, if we authenticate a principal, its title is set:
+Now, if we authenticate a principal, its title is set::
 
   >>> principal = pau.authenticate(request)
   >>> principal.title
@@ -211,7 +211,7 @@
 order specified in the PAU's authenticatorPlugins attribute, to authenticate
 a set of credentials.
 
-To illustrate, we'll create another authenticator:
+To illustrate, we'll create another authenticator::
 
   >>> class MyAuthenticatorPlugin2(MyAuthenticatorPlugin):
   ...
@@ -223,34 +223,34 @@
 
   >>> provideUtility(MyAuthenticatorPlugin2(), name='My Authenticator Plugin 2')
 
-If we put it before the original authenticator:
+If we put it before the original authenticator::
 
   >>> pau.authenticatorPlugins = (
   ...     'My Authenticator Plugin 2',
   ...     'My Authenticator Plugin')
 
-Then it will be given the first opportunity to authenticate a request:
+Then it will be given the first opportunity to authenticate a request::
 
   >>> pau.authenticate(TestRequest(credentials='secretcode'))
   Principal('xyz_black')
 
-If neither plugins can authenticate, pau returns None:
+If neither plugins can authenticate, pau returns None::
 
   >>> print pau.authenticate(TestRequest(credentials='let me in!!'))
   None
 
-When we change the order of the authenticator plugins:
+When we change the order of the authenticator plugins::
 
   >>> pau.authenticatorPlugins = (
   ...     'My Authenticator Plugin',
   ...     'My Authenticator Plugin 2')
 
-we see that our original plugin is now acting first:
+we see that our original plugin is now acting first::
 
   >>> pau.authenticate(TestRequest(credentials='secretcode'))
   Principal('xyz_bob')
 
-The second plugin, however, gets a chance to authenticate if first does not:
+The second plugin, however, gets a chance to authenticate if first does not::
 
   >>> pau.authenticate(TestRequest(credentials='hiddenkey'))
   Principal('xyz_white')
@@ -260,7 +260,7 @@
 
 As with with authenticators, we can specify multiple credentials plugins. To
 illustrate, we'll create a credentials plugin that extracts credentials from
-a request form:
+a request form::
 
   >>> class FormCredentialsPlugin:
   ...
@@ -278,14 +278,14 @@
   >>> provideUtility(FormCredentialsPlugin(),
   ...                name='Form Credentials Plugin')
 
-and insert the new credentials plugin before the existing plugin:
+and insert the new credentials plugin before the existing plugin::
 
   >>> pau.credentialsPlugins = (
   ...     'Form Credentials Plugin',
   ...     'My Credentials Plugin')
 
 The PAU will use each plugin in order to try and obtain credentials from a
-request.
+request::
 
   >>> pau.authenticate(TestRequest(credentials='secretcode',
   ...                              form={'my_credentials': 'hiddenkey'}))
@@ -305,12 +305,12 @@
 
  - Succeeded in authenticating with 'My Authenticator Plugin 2'
 
-Let's try a different scenario:
+Let's try a different scenario::
 
   >>> pau.authenticate(TestRequest(credentials='secretcode'))
   Principal('xyz_bob')
 
-In this case, the PAU went through these steps:
+In this case, the PAU went through these steps::
 
   - Get credentials using 'Form Credentials Plugin'
 
@@ -322,7 +322,7 @@
 
   - Succeeded in authenticating with 'My Authenticator Plugin'
 
-Let's try a slightly more complex scenario:
+Let's try a slightly more complex scenario::
 
   >>> pau.authenticate(TestRequest(credentials='hiddenkey',
   ...                              form={'my_credentials': 'bogusvalue'}))
@@ -360,7 +360,7 @@
 As a component that provides IAuthentication, a PAU lets you lookup a
 principal with a principal ID. The PAU looks up a principal by delegating to
 its authenticators. In our example, none of the authenticators implement this
-search capability, so when we look for a principal:
+search capability, so when we look for a principal::
 
   >>> print pau.getPrincipal('xyz_bob')
   Traceback (most recent call last):
@@ -376,7 +376,7 @@
 
 For a PAU to support search, it needs to be configured with one or more
 authenticator plugins that support search. To illustrate, we'll create a new
-authenticator:
+authenticator::
 
   >>> class SearchableAuthenticatorPlugin:
   ...
@@ -403,26 +403,26 @@
 where an authenticator may opt to not perform one of these two functions, they
 are less typical.
 
-As with any plugin, we need to register it as a utility:
+As with any plugin, we need to register it as a utility::
 
   >>> searchable = SearchableAuthenticatorPlugin()
   >>> provideUtility(searchable, name='Searchable Authentication Plugin')
 
-We'll now configure the PAU to use only the searchable authenticator:
+We'll now configure the PAU to use only the searchable authenticator::
 
   >>> pau.authenticatorPlugins = ('Searchable Authentication Plugin',)
 
-and add some principals to the authenticator:
+and add some principals to the authenticator::
 
   >>> searchable.add('bob', 'Bob', 'A nice guy', 'b0b')
   >>> searchable.add('white', 'White Spy', 'Sneaky', 'deathtoblack')
 
-Now when we ask the PAU to find a principal:
+Now when we ask the PAU to find a principal::
 
   >>> pau.getPrincipal('xyz_bob')
   Principal('xyz_bob')
 
-but only those it knows about:
+but only those it knows about::
 
   >>> print pau.getPrincipal('black')
   Traceback (most recent call last):
@@ -433,7 +433,7 @@
 
 As evident in the authenticator's 'createFoundPrincipal' method (see above),
 a FoundPrincipalCreatedEvent is published when the authenticator finds a
-principal on behalf of PAU's 'getPrincipal':
+principal on behalf of PAU's 'getPrincipal'::
 
   >>> clearEvents()
   >>> principal = pau.getPrincipal('xyz_white')
@@ -447,7 +447,7 @@
   PrincipalInfo('white')
 
 The info has an authenticatorPlugin, but no credentialsPlugin, since none was
-used.
+used::
 
   >>> event.info.credentialsPlugin is None
   True
@@ -456,7 +456,7 @@
 
 As we have seen with authenticated principals, it is common to subscribe to
 principal created events to add information to the newly created principal.
-In this case, we need to subscribe to IFoundPrincipalCreated events:
+In this case, we need to subscribe to IFoundPrincipalCreated events::
 
   >>> subscribe([interfaces.IFoundPrincipalCreated], None, add_info)
 
@@ -470,23 +470,23 @@
 find a principal. If the first authenticator plugin can't find the requested
 principal, the next plugin is used, and so on.
 
-To illustrate, we'll create and register a second searchable authenticator:
+To illustrate, we'll create and register a second searchable authenticator::
 
   >>> searchable2 = SearchableAuthenticatorPlugin()
   >>> provideUtility(searchable2, name='Searchable Authentication Plugin 2')
 
-and add a principal to it:
+and add a principal to it::
 
   >>> searchable.add('black', 'Black Spy', 'Also sneaky', 'deathtowhite')
 
 When we configure the PAU to use both searchable authenticators (note the
-order):
+order)::
 
   >>> pau.authenticatorPlugins = (
   ...     'Searchable Authentication Plugin 2',
   ...     'Searchable Authentication Plugin')
 
-we see how the PAU uses both plugins:
+we see how the PAU uses both plugins::
 
   >>> pau.getPrincipal('xyz_white')
   Principal('xyz_white')
@@ -496,19 +496,19 @@
 
 If more than one plugin know about the same principal ID, the first plugin is
 used and the remaining are not delegated to. To illustrate, we'll add
-another principal with the same ID as an existing principal:
+another principal with the same ID as an existing principal::
 
   >>> searchable2.add('white', 'White Rider', '', 'r1der')
   >>> pau.getPrincipal('xyz_white').title
   'White Rider'
 
-If we change the order of the plugins:
+If we change the order of the plugins::
 
   >>> pau.authenticatorPlugins = (
   ...     'Searchable Authentication Plugin',
   ...     'Searchable Authentication Plugin 2')
 
-we get a different principal for ID 'white':
+we get a different principal for ID 'white'::
 
   >>> pau.getPrincipal('xyz_white').title
   'White Spy'
@@ -537,7 +537,7 @@
 perform any action when asked to challenge (see above the 'challenge' methods).
 
 To illustrate challenges, we'll subclass an existing credentials plugin and
-do something in its 'challenge':
+do something in its 'challenge'::
 
   >>> class LoginFormCredentialsPlugin(FormCredentialsPlugin):
   ...
@@ -551,7 +551,7 @@
 This plugin handles a challenge by redirecting the response to a login form.
 It returns True to signal to the PAU that it handled the challenge.
 
-We will now create and register a couple of these plugins:
+We will now create and register a couple of these plugins::
 
   >>> provideUtility(LoginFormCredentialsPlugin('simplelogin.html'),
   ...                name='Simple Login Form Plugin')
@@ -559,36 +559,36 @@
   >>> provideUtility(LoginFormCredentialsPlugin('advancedlogin.html'),
   ...                name='Advanced Login Form Plugin')
 
-and configure the PAU to use them:
+and configure the PAU to use them::
 
   >>> pau.credentialsPlugins = (
   ...     'Simple Login Form Plugin',
   ...     'Advanced Login Form Plugin')
 
-Now when we call 'unauthorized' on the PAU:
+Now when we call 'unauthorized' on the PAU::
 
   >>> request = TestRequest()
   >>> pau.unauthorized(id=None, request=request)
 
-we see that the user is redirected to the simple login form:
+we see that the user is redirected to the simple login form::
 
   >>> request.response.getStatus()
   302
   >>> request.response.getHeader('location')
   'simplelogin.html'
 
-We can change the challenge policy by reordering the plugins:
+We can change the challenge policy by reordering the plugins::
 
   >>> pau.credentialsPlugins = (
   ...     'Advanced Login Form Plugin',
   ...     'Simple Login Form Plugin')
 
-Now when we call 'unauthorized':
+Now when we call 'unauthorized'::
 
   >>> request = TestRequest()
   >>> pau.unauthorized(id=None, request=request)
 
-the advanced plugin is used because it's first:
+the advanced plugin is used because it's first::
 
   >>> request.response.getStatus()
   302
@@ -747,7 +747,7 @@
   >>> list(pau.getQueriables()) # doctest: +ELLIPSIS
   [('Queriable', ...QuerySchemaSearchAdapter object...)]
 
-We can use this queriable to search for our principal:
+We can use this queriable to search for our principal::
 
   >>> queriable = list(pau.getQueriables())[0][1]
   >>> list(queriable.search('not-used'))
@@ -761,3 +761,61 @@
 
 The result does not include the PAU prefix. The prepending of the prefix is
 handled by the PluggableAuthenticationQueriable.
+  
+
+Queryiable plugins can provide the ILocation interface. In this case the
+QuerySchemaSearchAdapter's __parent__ is the same as the __parent__ of the
+plugin::
+
+  >>> import zope.location.interfaces
+  >>> class LocatedQueriableAuthenticatorPlugin(QueriableAuthenticatorPlugin):
+  ...
+  ...     interface.implements(zope.location.interfaces.ILocation)
+  ...
+  ...     __parent__ = __name__ = None
+  ...
+  >>> import zope.app.component.hooks
+  >>> site = zope.app.component.hooks.getSite()
+  >>> plugin = LocatedQueriableAuthenticatorPlugin()
+  >>> plugin.__parent__ = site
+  >>> plugin.__name__ = 'localname'
+  >>> provideUtility(plugin,
+  ...                provides=interfaces.IAuthenticatorPlugin,
+  ...                name='location-queriable')
+  >>> pau.authenticatorPlugins = ('location-queriable',)
+
+We have one queriable again::
+
+  >>> queriables = list(pau.getQueriables())
+  >>> queriables  # doctest: +ELLIPSIS
+  [('location-queriable', ...QuerySchemaSearchAdapter object...)]
+
+The queriable's __parent__ is the site as set above::
+
+  >>> queriable = queriables[0][1]
+  >>> queriable.__parent__ is site
+  True
+
+If the queriable provides ILocation but is not actually locatable (i.e. the
+parent is None) the pau itself becomes the parent::
+
+
+  >>> plugin = LocatedQueriableAuthenticatorPlugin()
+  >>> provideUtility(plugin,
+  ...                provides=interfaces.IAuthenticatorPlugin,
+  ...                name='location-queriable-wo-parent')
+  >>> pau.authenticatorPlugins = ('location-queriable-wo-parent',)
+
+We have one queriable again::
+
+  >>> queriables = list(pau.getQueriables())
+  >>> queriables  # doctest: +ELLIPSIS
+  [('location-queriable-wo-parent', ...QuerySchemaSearchAdapter object...)]
+  
+And the parent is the pau::
+
+  >>> queriable = queriables[0][1]
+  >>> queriable.__parent__  # doctest: +ELLIPSIS
+  <zope.app.authentication.authentication.PluggableAuthentication object at 0x...>
+  >>> queriable.__parent__ is pau
+  True

Modified: zope.app.authentication/trunk/src/zope/app/authentication/authentication.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/authentication.py	2007-06-25 14:22:46 UTC (rev 77043)
+++ zope.app.authentication/trunk/src/zope/app/authentication/authentication.py	2007-06-25 14:32:12 UTC (rev 77044)
@@ -167,7 +167,11 @@
         ILocation)
 
     def __init__(self, authplugin, pau):
-        if ILocation.providedBy(authplugin):
+        if (ILocation.providedBy(authplugin) and
+            authplugin.__parent__ is not None):
+            # Checking explicitly for the parent, because providing ILocation
+            # basically means that the object *could* be located. It doesn't
+            # say the object must be located.
             self.__parent__ = authplugin.__parent__
             self.__name__ = authplugin.__name__
         else:



More information about the Checkins mailing list