[Checkins] SVN: zope.password/ Split off passwod managers from zope.app.authentication.
Dan Korostelev
nadako at gmail.com
Fri Mar 6 06:47:05 EST 2009
Log message for revision 97560:
Split off passwod managers from zope.app.authentication.
Changed:
A zope.password/
A zope.password/trunk/
U zope.password/trunk/CHANGES.txt
U zope.password/trunk/README.txt
U zope.password/trunk/buildout.cfg
U zope.password/trunk/setup.py
D zope.password/trunk/src/zope/app/
A zope.password/trunk/src/zope/password/
U zope.password/trunk/src/zope/password/README.txt
D zope.password/trunk/src/zope/password/SETUP.cfg
U zope.password/trunk/src/zope/password/__init__.py
D zope.password/trunk/src/zope/password/authentication.py
D zope.password/trunk/src/zope/password/browser/
D zope.password/trunk/src/zope/password/configure.zcml
A zope.password/trunk/src/zope/password/configure.zcml
D zope.password/trunk/src/zope/password/ftesting.zcml
D zope.password/trunk/src/zope/password/ftpplugins.py
D zope.password/trunk/src/zope/password/ftpplugins.zcml
D zope.password/trunk/src/zope/password/generic.py
D zope.password/trunk/src/zope/password/groupfolder.py
D zope.password/trunk/src/zope/password/groupfolder.txt
D zope.password/trunk/src/zope/password/groupfolder.zcml
D zope.password/trunk/src/zope/password/httpplugins.py
D zope.password/trunk/src/zope/password/httpplugins.zcml
D zope.password/trunk/src/zope/password/i18n.py
D zope.password/trunk/src/zope/password/idpicker.py
U zope.password/trunk/src/zope/password/interfaces.py
U zope.password/trunk/src/zope/password/password.py
D zope.password/trunk/src/zope/password/password.zcml
D zope.password/trunk/src/zope/password/placelesssetup.py
D zope.password/trunk/src/zope/password/principalfolder.py
D zope.password/trunk/src/zope/password/principalfolder.txt
D zope.password/trunk/src/zope/password/principalfolder.zcml
D zope.password/trunk/src/zope/password/session.py
D zope.password/trunk/src/zope/password/session.zcml
D zope.password/trunk/src/zope/password/testing.py
A zope.password/trunk/src/zope/password/testing.py
U zope.password/trunk/src/zope/password/tests.py
D zope.password/trunk/src/zope/password/vocabulary.py
D zope.password/trunk/src/zope/password/vocabulary.txt
D zope.password/trunk/src/zope/password/zope.app.authentication-configure.zcml
-=-
Modified: zope.password/trunk/CHANGES.txt
===================================================================
--- zope.app.authentication/trunk/CHANGES.txt 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/CHANGES.txt 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,62 +1,10 @@
=======
-Changes
+CHANGES
=======
-3.5.0a3 (unreleased)
---------------------
-
-- ...
-
-3.5.0a2 (2009-02-01)
---------------------
-
-* Make old encoded passwords really work.
-
-3.5.0a1 (2009-01-31)
---------------------
-
-* Use ``zope.container`` instead of ``zope.app.container``.
-
-* Encoded passwords are now stored with a prefix ({MD5}, {SHA1},
- {SSHA}) indicating the used encoding schema. Old (encoded) passwords
- can still be used.
-
-* Add an SSHA password manager that is compatible with standard LDAP
- passwords. As this encoding gives better security agains dictionary
- attacks, users are encouraged to switch to this new password schema.
-
-* InternalPrincipal now uses SSHA password manager by default.
-
-3.4.4 (2008-12-12)
+3.5.0 (unreleased)
------------------
-* Depend on zope.session instead of zope.app.session. The first one
- currently has all functionality we need.
-* Fix deprecation warnings for ``md5`` and ``sha`` on Python 2.6.
-
-3.4.3 (2008-08-07)
-------------------
-
-* No changes. Retag for correct release on PyPI.
-
-3.4.2 (2008-07-09)
--------------------
-
-* Make it compatible with zope.app.container 3.6.1 and 3.5.4 changes,
- Changed ``super(BTreeContainer, self).__init__()`` to
- ``super(GroupFolder, self).__init__()`` in ``GroupFolder`` class.
-
-3.4.1 (2007-10-24)
-------------------
-
-* Avoid deprecation warning.
-
-3.4.0 (2007-10-11)
-------------------
-
-* Updated package meta-data.
-
-3.4.0b1 (2007-09-27)
---------------------
-
-* First release independent of Zope.
+First release. This package was splitted off from ``zope.app.authentication``
+to separate password manager functionality that is greatly re-usable without
+any bit of ``zope.app.authentication`` and to reduce its dependencies.
Modified: zope.password/trunk/README.txt
===================================================================
--- zope.app.authentication/trunk/README.txt 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/README.txt 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,6 +1 @@
-==========================
- Pluggable Authentication
-==========================
-
-This package provides a flexible and pluggable authentication utility for Zope
-3. Several common plugins are provided.
+See the src/zope/password/README.txt file.
Modified: zope.password/trunk/buildout.cfg
===================================================================
--- zope.app.authentication/trunk/buildout.cfg 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/buildout.cfg 2009-03-06 11:47:04 UTC (rev 97560)
@@ -4,4 +4,4 @@
[test]
recipe = zc.recipe.testrunner
-eggs = zope.app.authentication [test]
+eggs = zope.password [test]
Modified: zope.password/trunk/setup.py
===================================================================
--- zope.app.authentication/trunk/setup.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/setup.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -11,36 +11,24 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Setup for zope.app.authentication package
+"""Setup for zope.password package
$Id$
"""
-import os
from setuptools import setup, find_packages
-def read(*rnames):
- return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
-setup(name='zope.app.authentication',
- version = '3.5.0a3dev',
+setup(name='zope.password',
+ version='3.5.0dev',
author='Zope Corporation and Contributors',
author_email='zope-dev at zope.org',
- description='Pluggable Authentication Utility',
+ description='Password encoding and checking utilities',
long_description=(
- read('README.txt')
+ open('README.txt').read(),
+ '\n\n' +
- 'Detailed Documentation\n' +
- '----------------------\n'
- + '\n' +
- read('src', 'zope', 'app', 'authentication', 'README.txt')
- + '\n\n' +
- read('src', 'zope', 'app', 'authentication', 'principalfolder.txt')
- + '\n\n' +
- read('src', 'zope', 'app', 'authentication', 'vocabulary.txt')
- + '\n\n' +
- read('CHANGES.txt')
+ open('CHANGES.txt').read()
),
- url='http://pypi.python.org/pypi/zope.app.authentication',
+ url='http://pypi.python.org/pypi/zope.password',
license='ZPL 2.1',
classifiers = [
'Development Status :: 5 - Production/Stable',
@@ -52,33 +40,13 @@
'Operating System :: OS Independent',
'Topic :: Internet :: WWW/HTTP',
'Framework :: Zope3'],
- keywords='zope3 authentication pluggable principal group',
+ keywords='zope3 zope authentication password',
packages=find_packages('src'),
package_dir = {'': 'src'},
- extras_require=dict(test=['zope.app.testing',
- 'zope.app.securitypolicy',
- 'zope.app.zcmlfiles',
- 'zope.securitypolicy',
- 'zope.testbrowser']),
- namespace_packages=['zope', 'zope.app'],
+ extras_require=dict(test=['zope.testing']),
+ namespace_packages=['zope'],
install_requires=['setuptools',
- 'zope.app.component',
- 'zope.app.container',
- 'zope.app.form',
- 'zope.app.security',
- 'zope.dublincore',
- 'zope.event',
- 'zope.exceptions',
- 'zope.i18n',
- 'zope.i18nmessageid',
'zope.interface',
- 'zope.location',
- 'zope.publisher',
- 'zope.schema',
- 'zope.security',
- 'zope.session',
- 'zope.traversing',
- 'ZODB3',
],
include_package_data = True,
zip_safe = False,
Modified: zope.password/trunk/src/zope/password/README.txt
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/README.txt 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/README.txt 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,822 +1,38 @@
-================================
-Pluggable-Authentication Utility
-================================
+================
+Password Manager
+================
-The Pluggable-Authentication Utility (PAU) provides a framework for
-authenticating principals and associating information with them. It uses
-plugins and subscribers to get its work done.
+This package provides a password manager mechanism. Password manager is an
+utility object that can encode and check encoded passwords. Beyond the generic
+interface, this package also provides four implementations:
-For a pluggable-authentication utility to be used, it should be
-registered as a utility providing the
-`zope.app.security.interfaces.IAuthentication` interface.
+ * PlainTextPasswordManager - the most simple and the less secure one. It does
+ not do any password encoding and simply checks password by string equality.
+ It's useful in tests or as a base class for more secure implementations.
-Authentication
---------------
+ * MD5PasswordManager - a password manager that uses MD5 algorithm to encode
+ passwords. It adds salt to the encoded password, but the salt is not used
+ for encoding the password itself, so the use of salt in it is purely
+ cosmetical. It's generally weak against dictionary attacks.
+
+ * SHA1PasswordManager - a password manager that uses SHA1 algorithm to encode
+ passwords. It has the same salt weakness as the MD5PasswordManager.
+
+ * SSHAPasswordManager - the most secure password manager that is strong against
+ dictionary attacks. It's basically SHA1-encoding password manager which also
+ incorporates a salt into the password when encoding it. This password manager
+ is compatible with passwords used in LDAP databases.
-The primary job of PAU is to authenticate principals. It uses two types of
-plug-ins in its work:
+It is strongly recommended to use SSHAPasswordManager, as it's the most secure
+one.
- - Credentials Plugins
+Usage
+-----
- - Authenticator Plugins
+It's very easy to use password managers. The ``zope.password.interfaces.IPasswordManager``
+interface defines only two methods:
-Credentials plugins are responsible for extracting user credentials from a
-request. A credentials plugin may in some cases issue a 'challenge' to obtain
-credentials. For example, a 'session' credentials plugin reads credentials
-from a session (the "extraction"). If it cannot find credentials, it will
-redirect the user to a login form in order to provide them (the "challenge").
-
-Authenticator plugins are responsible for authenticating the credentials
-extracted by a credentials plugin. They are also typically able to create
-principal objects for credentials they successfully authenticate.
-
-Given a request object, the PAU returns a principal object, if it can. The PAU
-does this by first iterateing through its credentials plugins to obtain a
-set of credentials. If it gets credentials, it iterates through its
-authenticator plugins to authenticate them.
-
-If an authenticator succeeds in authenticating a set of credentials, the PAU
-uses the authenticator to create a principal corresponding to the credentials.
-The authenticator notifies subscribers if an authenticated principal is
-created. Subscribers are responsible for adding data, especially groups, to
-the principal. Typically, if a subscriber adds data, it should also add
-corresponding interface declarations.
-
-Simple Credentials Plugin
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To illustrate, we'll create a simple credentials plugin::
-
- >>> from zope import interface
- >>> from zope.app.authentication import interfaces
-
- >>> class MyCredentialsPlugin(object):
- ...
- ... interface.implements(interfaces.ICredentialsPlugin)
- ...
- ... def extractCredentials(self, request):
- ... return request.get('credentials')
- ...
- ... def challenge(self, request):
- ... pass # challenge is a no-op for this plugin
- ...
- ... 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::
-
- >>> myCredentialsPlugin = MyCredentialsPlugin()
- >>> provideUtility(myCredentialsPlugin, name='My Credentials Plugin')
-
-Simple Authenticator Plugin
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Next we'll create a simple authenticator plugin. For our plugin, we'll need
-an implementation of IPrincipalInfo::
-
- >>> class PrincipalInfo(object):
- ...
- ... interface.implements(interfaces.IPrincipalInfo)
- ...
- ... def __init__(self, id, title, description):
- ... self.id = id
- ... self.title = title
- ... self.description = description
- ...
- ... def __repr__(self):
- ... return 'PrincipalInfo(%r)' % self.id
-
-Our authenticator uses this type when it creates a principal info::
-
- >>> class MyAuthenticatorPlugin(object):
- ...
- ... interface.implements(interfaces.IAuthenticatorPlugin)
- ...
- ... def authenticateCredentials(self, credentials):
- ... if credentials == 'secretcode':
- ... return PrincipalInfo('bob', 'Bob', '')
- ...
- ... def principalInfo(self, id):
- ... pass # plugin not currently supporting search
-
-As with the credentials plugin, the authenticator plugin must be registered
-as a named utility::
-
- >>> myAuthenticatorPlugin = MyAuthenticatorPlugin()
- >>> provideUtility(myAuthenticatorPlugin, name='My Authenticator Plugin')
-
-Principal Factories
-~~~~~~~~~~~~~~~~~~~
-
-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::
-
- >>> from zope.app.authentication import principalfolder
- >>> provideAdapter(principalfolder.AuthenticatedPrincipalFactory)
- >>> provideAdapter(principalfolder.FoundPrincipalFactory)
-
-For more information on these factories, see their docstrings.
-
-Configuring a PAU
-~~~~~~~~~~~~~~~~~
-
-Finally, we'll create the PAU itself::
-
- >>> from zope.app import authentication
- >>> pau = authentication.PluggableAuthentication('xyz_')
-
-and configure it with the two plugins::
-
- >>> pau.credentialsPlugins = ('My Credentials Plugin', )
- >>> pau.authenticatorPlugins = ('My Authenticator Plugin', )
-
-Using the PAU to Authenticate
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-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::
-
- >>> print pau.authenticate(TestRequest(credentials='let me in!'))
- None
-
-However, if we provide the proper credentials::
-
- >>> request = TestRequest(credentials='secretcode')
- >>> principal = pau.authenticate(request)
- >>> principal
- Principal('xyz_bob')
-
-we get an authenticated principal.
-
-Authenticated Principal Creates Events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We can verify that the appropriate event was published::
-
- >>> [event] = getEvents(interfaces.IAuthenticatedPrincipalCreated)
- >>> event.principal is principal
- True
- >>> event.info
- PrincipalInfo('bob')
- >>> event.request is request
- True
-
-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::
-
- >>> event.info.title
- 'Bob'
- >>> event.info.id # does not include pau prefix
- 'bob'
- >>> event.info.description
- ''
-
-It is also decorated with two other attributes, credentialsPlugin and
-authenticatorPlugin: these are the plugins used to extract credentials for and
-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::
-
- >>> event.info.credentialsPlugin is myCredentialsPlugin
- True
- >>> event.info.authenticatorPlugin is myAuthenticatorPlugin
- True
-
-Normally, we provide subscribers to these events that add additional
-information to the principal. For example, we'll add one that sets
-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::
-
- >>> principal = pau.authenticate(request)
- >>> principal.title
- 'Bob'
-
-Multiple Authenticator Plugins
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The PAU works with multiple authenticator plugins. It uses each plugin, in the
-order specified in the PAU's authenticatorPlugins attribute, to authenticate
-a set of credentials.
-
-To illustrate, we'll create another authenticator::
-
- >>> class MyAuthenticatorPlugin2(MyAuthenticatorPlugin):
- ...
- ... def authenticateCredentials(self, credentials):
- ... if credentials == 'secretcode':
- ... return PrincipalInfo('black', 'Black Spy', '')
- ... elif credentials == 'hiddenkey':
- ... return PrincipalInfo('white', 'White Spy', '')
-
- >>> provideUtility(MyAuthenticatorPlugin2(), name='My Authenticator Plugin 2')
-
-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::
-
- >>> pau.authenticate(TestRequest(credentials='secretcode'))
- Principal('xyz_black')
-
-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::
-
- >>> pau.authenticatorPlugins = (
- ... 'My Authenticator Plugin',
- ... 'My Authenticator Plugin 2')
-
-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::
-
- >>> pau.authenticate(TestRequest(credentials='hiddenkey'))
- Principal('xyz_white')
-
-Multiple Credentials Plugins
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-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::
-
- >>> class FormCredentialsPlugin:
- ...
- ... interface.implements(interfaces.ICredentialsPlugin)
- ...
- ... def extractCredentials(self, request):
- ... return request.form.get('my_credentials')
- ...
- ... def challenge(self, request):
- ... pass
- ...
- ... def logout(request):
- ... pass
-
- >>> provideUtility(FormCredentialsPlugin(),
- ... name='Form Credentials 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::
-
- >>> pau.authenticate(TestRequest(credentials='secretcode',
- ... form={'my_credentials': 'hiddenkey'}))
- Principal('xyz_white')
-
-In this case, the first credentials plugin succeeded in getting credentials
-from the form and the second authenticator was able to authenticate the
-credentials. Specifically, the PAU went through these steps:
-
- - Get credentials using 'Form Credentials Plugin'
-
- - Got 'hiddenkey' credentials using 'Form Credentials Plugin', try to
- authenticate using 'My Authenticator Plugin'
-
- - Failed to authenticate 'hiddenkey' with 'My Authenticator Plugin', try
- 'My Authenticator Plugin 2'
-
- - Succeeded in authenticating with 'My Authenticator Plugin 2'
-
-Let's try a different scenario::
-
- >>> pau.authenticate(TestRequest(credentials='secretcode'))
- Principal('xyz_bob')
-
-In this case, the PAU went through these steps::
-
- - Get credentials using 'Form Credentials Plugin'
-
- - Failed to get credentials using 'Form Credentials Plugin', try
- 'My Credentials Plugin'
-
- - Got 'scecretcode' credentials using 'My Credentials Plugin', try to
- authenticate using 'My Authenticator Plugin'
-
- - Succeeded in authenticating with 'My Authenticator Plugin'
-
-Let's try a slightly more complex scenario::
-
- >>> pau.authenticate(TestRequest(credentials='hiddenkey',
- ... form={'my_credentials': 'bogusvalue'}))
- Principal('xyz_white')
-
-This highlights PAU's ability to use multiple plugins for authentication:
-
- - Get credentials using 'Form Credentials Plugin'
-
- - Got 'bogusvalue' credentials using 'Form Credentials Plugin', try to
- authenticate using 'My Authenticator Plugin'
-
- - Failed to authenticate 'boguskey' with 'My Authenticator Plugin', try
- 'My Authenticator Plugin 2'
-
- - Failed to authenticate 'boguskey' with 'My Authenticator Plugin 2' --
- there are no more authenticators to try, so lets try the next credentials
- plugin for some new credentials
-
- - Get credentials using 'My Credentials Plugin'
-
- - Got 'hiddenkey' credentials using 'My Credentials Plugin', try to
- authenticate using 'My Authenticator Plugin'
-
- - Failed to authenticate 'hiddenkey' using 'My Authenticator Plugin', try
- 'My Authenticator Plugin 2'
-
- - Succeeded in authenticating with 'My Authenticator Plugin 2' (shouts and
- cheers!)
-
-
-Principal Searching
--------------------
-
-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::
-
- >>> print pau.getPrincipal('xyz_bob')
- Traceback (most recent call last):
- PrincipalLookupError: bob
-
- >>> print pau.getPrincipal('white')
- Traceback (most recent call last):
- PrincipalLookupError: white
-
- >>> print pau.getPrincipal('black')
- Traceback (most recent call last):
- PrincipalLookupError: black
-
-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::
-
- >>> class SearchableAuthenticatorPlugin:
- ...
- ... interface.implements(interfaces.IAuthenticatorPlugin)
- ...
- ... def __init__(self):
- ... self.infos = {}
- ... self.ids = {}
- ...
- ... def principalInfo(self, id):
- ... return self.infos.get(id)
- ...
- ... def authenticateCredentials(self, credentials):
- ... id = self.ids.get(credentials)
- ... if id is not None:
- ... return self.infos[id]
- ...
- ... def add(self, id, title, description, credentials):
- ... self.infos[id] = PrincipalInfo(id, title, description)
- ... self.ids[credentials] = id
-
-This class is typical of an authenticator plugin. It can both authenticate
-principals and find principals given a ID. While there are cases
-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::
-
- >>> searchable = SearchableAuthenticatorPlugin()
- >>> provideUtility(searchable, name='Searchable Authentication Plugin')
-
-We'll now configure the PAU to use only the searchable authenticator::
-
- >>> pau.authenticatorPlugins = ('Searchable Authentication Plugin',)
-
-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::
-
- >>> pau.getPrincipal('xyz_bob')
- Principal('xyz_bob')
-
-but only those it knows about::
-
- >>> print pau.getPrincipal('black')
- Traceback (most recent call last):
- PrincipalLookupError: black
-
-Found Principal Creates Events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-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'::
-
- >>> clearEvents()
- >>> principal = pau.getPrincipal('xyz_white')
- >>> principal
- Principal('xyz_white')
-
- >>> [event] = getEvents(interfaces.IFoundPrincipalCreated)
- >>> event.principal is principal
- True
- >>> event.info
- PrincipalInfo('white')
-
-The info has an authenticatorPlugin, but no credentialsPlugin, since none was
-used::
-
- >>> event.info.credentialsPlugin is None
- True
- >>> event.info.authenticatorPlugin is searchable
- True
-
-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::
-
- >>> subscribe([interfaces.IFoundPrincipalCreated], None, add_info)
-
-Now when a principal is created as a result of a search, it's title and
-description will be set (by the add_info handler function).
-
-Multiple Authenticator Plugins
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As with the other operations we've seen, the PAU uses multiple plugins to
-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::
-
- >>> searchable2 = SearchableAuthenticatorPlugin()
- >>> provideUtility(searchable2, name='Searchable Authentication Plugin 2')
-
-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)::
-
- >>> pau.authenticatorPlugins = (
- ... 'Searchable Authentication Plugin 2',
- ... 'Searchable Authentication Plugin')
-
-we see how the PAU uses both plugins::
-
- >>> pau.getPrincipal('xyz_white')
- Principal('xyz_white')
-
- >>> pau.getPrincipal('xyz_black')
- Principal('xyz_black')
-
-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::
-
- >>> searchable2.add('white', 'White Rider', '', 'r1der')
- >>> pau.getPrincipal('xyz_white').title
- 'White Rider'
-
-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'::
-
- >>> pau.getPrincipal('xyz_white').title
- 'White Spy'
-
-
-Issuing a Challenge
--------------------
-
-Part of PAU's IAuthentication contract is to challenge the user for
-credentials when its 'unauthorized' method is called. The need for this
-functionality is driven by the following use case:
-
- - A user attempts to perform an operation he is not authorized to perform.
-
- - A handler responds to the unauthorized error by calling IAuthentication
- 'unauthorized'.
-
- - The authentication component (in our case, a PAU) issues a challenge to
- the user to collect new credentials (typically in the form of logging in
- as a new user).
-
-The PAU handles the credentials challenge by delegating to its credentials
-plugins.
-
-Currently, the PAU is configured with the credentials plugins that don't
-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'::
-
- >>> class LoginFormCredentialsPlugin(FormCredentialsPlugin):
- ...
- ... def __init__(self, loginForm):
- ... self.loginForm = loginForm
- ...
- ... def challenge(self, request):
- ... request.response.redirect(self.loginForm)
- ... return True
-
-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::
-
- >>> provideUtility(LoginFormCredentialsPlugin('simplelogin.html'),
- ... name='Simple Login Form Plugin')
-
- >>> provideUtility(LoginFormCredentialsPlugin('advancedlogin.html'),
- ... name='Advanced Login Form Plugin')
-
-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::
-
- >>> request = TestRequest()
- >>> pau.unauthorized(id=None, request=request)
-
-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::
-
- >>> pau.credentialsPlugins = (
- ... 'Advanced Login Form Plugin',
- ... 'Simple Login Form Plugin')
-
-Now when we call 'unauthorized'::
-
- >>> request = TestRequest()
- >>> pau.unauthorized(id=None, request=request)
-
-the advanced plugin is used because it's first::
-
- >>> request.response.getStatus()
- 302
- >>> request.response.getHeader('location')
- 'advancedlogin.html'
-
-Challenge Protocols
-~~~~~~~~~~~~~~~~~~~
-
-Sometimes, we want multiple challengers to work together. For example, the
-HTTP specification allows multiple challenges to be issued in a response. A
-challenge plugin can provide a `challengeProtocol` attribute that effectively
-groups related plugins together for challenging. If a plugin returns `True`
-from its challenge and provides a non-None challengeProtocol, subsequent
-plugins in the credentialsPlugins list that have the same challenge protocol
-will also be used to challenge.
-
-Without a challengeProtocol, only the first plugin to succeed in a challenge
-will be used.
-
-Let's look at an example. We'll define a new plugin that specifies an
-'X-Challenge' protocol::
-
- >>> class XChallengeCredentialsPlugin(FormCredentialsPlugin):
- ...
- ... challengeProtocol = 'X-Challenge'
- ...
- ... def __init__(self, challengeValue):
- ... self.challengeValue = challengeValue
- ...
- ... def challenge(self, request):
- ... value = self.challengeValue
- ... existing = request.response.getHeader('X-Challenge', '')
- ... if existing:
- ... value += ' ' + existing
- ... request.response.setHeader('X-Challenge', value)
- ... return True
-
-and register a couple instances as utilities::
-
- >>> provideUtility(XChallengeCredentialsPlugin('basic'),
- ... name='Basic X-Challenge Plugin')
-
- >>> provideUtility(XChallengeCredentialsPlugin('advanced'),
- ... name='Advanced X-Challenge Plugin')
-
-When we use both plugins with the PAU::
-
- >>> pau.credentialsPlugins = (
- ... 'Basic X-Challenge Plugin',
- ... 'Advanced X-Challenge Plugin')
-
-and call 'unauthorized'::
-
- >>> request = TestRequest()
- >>> pau.unauthorized(None, request)
-
-we see that both plugins participate in the challange, rather than just the
-first plugin::
-
- >>> request.response.getHeader('X-Challenge')
- 'advanced basic'
-
-
-Pluggable-Authentication Prefixes
----------------------------------
-
-Principal ids are required to be unique system wide. Plugins will often provide
-options for providing id prefixes, so that different sets of plugins provide
-unique ids within a PAU. If there are multiple pluggable-authentication
-utilities in a system, it's a good idea to give each PAU a unique prefix, so
-that principal ids from different PAUs don't conflict. We can provide a prefix
-when a PAU is created::
-
- >>> pau = authentication.PluggableAuthentication('mypau_')
- >>> pau.credentialsPlugins = ('My Credentials Plugin', )
- >>> pau.authenticatorPlugins = ('My Authenticator Plugin', )
-
-When we create a request and try to authenticate::
-
- >>> pau.authenticate(TestRequest(credentials='secretcode'))
- Principal('mypau_bob')
-
-Note that now, our principal's id has the pluggable-authentication
-utility prefix.
-
-We can still lookup a principal, as long as we supply the prefix::
-
- >> pau.getPrincipal('mypas_42')
- Principal('mypas_42', "{'domain': 42}")
-
- >> pau.getPrincipal('mypas_41')
- OddPrincipal('mypas_41', "{'int': 41}")
-
-
-Searching
----------
-
-PAU implements ISourceQueriables::
-
- >>> from zope.schema.interfaces import ISourceQueriables
- >>> ISourceQueriables.providedBy(pau)
- True
-
-This means a PAU can be used in a principal source vocabulary (Zope provides a
-sophisticated searching UI for principal sources).
-
-As we've seen, a PAU uses each of its authenticator plugins to locate a
-principal with a given ID. However, plugins may also provide the interface
-IQuerySchemaSearch to indicate they can be used in the PAU's principal search
-scheme.
-
-Currently, our list of authenticators::
-
- >>> pau.authenticatorPlugins
- ('My Authenticator Plugin',)
-
-does not include a queriable authenticator. PAU cannot therefore provide any
-queriables::
-
- >>> list(pau.getQueriables())
- []
-
-Before we illustrate how an authenticator is used by the PAU to search for
-principals, we need to setup an adapter used by PAU::
-
- >>> provideAdapter(
- ... authentication.authentication.QuerySchemaSearchAdapter,
- ... provides=interfaces.IQueriableAuthenticator)
-
-This adapter delegates search responsibility to an authenticator, but prepends
-the PAU prefix to any principal IDs returned in a search.
-
-Next, we'll create a plugin that provides a search interface::
-
- >>> class QueriableAuthenticatorPlugin(MyAuthenticatorPlugin):
- ...
- ... interface.implements(interfaces.IQuerySchemaSearch)
- ...
- ... schema = None
- ...
- ... def search(self, query, start=None, batch_size=None):
- ... yield 'foo'
- ...
-
-and install it as a plugin::
-
- >>> plugin = QueriableAuthenticatorPlugin()
- >>> provideUtility(plugin,
- ... provides=interfaces.IAuthenticatorPlugin,
- ... name='Queriable')
- >>> pau.authenticatorPlugins += ('Queriable',)
-
-Now, the PAU provides a single queriable::
-
- >>> list(pau.getQueriables()) # doctest: +ELLIPSIS
- [('Queriable', ...QuerySchemaSearchAdapter object...)]
-
-We can use this queriable to search for our principal::
-
- >>> queriable = list(pau.getQueriables())[0][1]
- >>> list(queriable.search('not-used'))
- ['mypau_foo']
-
-Note that the resulting principal ID includes the PAU prefix. Were we to search
-the plugin directly::
-
- >>> list(plugin.search('not-used'))
- ['foo']
-
-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 ...>
- >>> queriable.__parent__ is pau
- True
+ * encodePassword(password) - return encoded data for the given `password`
+ * checkPassword(storedPassword, password) - return whether the given `password`
+ coincide with the storedPassword, which is
+
\ No newline at end of file
Deleted: zope.password/trunk/src/zope/password/SETUP.cfg
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/SETUP.cfg 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/SETUP.cfg 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,5 +0,0 @@
-# Tell zpkg how to install the ZCML slugs.
-
-<data-files zopeskel/etc/package-includes>
- *-configure.zcml
-</data-files>
Modified: zope.password/trunk/src/zope/password/__init__.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/__init__.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/__init__.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2004 Zope Corporation and Contributors.
+# Copyright (c) 2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -11,10 +11,6 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Pluggable Authentication Utility
-
+"""
$Id$
"""
-
-import interfaces
-from zope.app.authentication.authentication import PluggableAuthentication
Deleted: zope.password/trunk/src/zope/password/authentication.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/authentication.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/authentication.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,186 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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 Utility implementation
-
-$Id$
-"""
-import zope.interface
-from zope import component
-from zope.schema.interfaces import ISourceQueriables
-from zope.location.interfaces import ILocation
-
-from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
-from zope.app.component import queryNextUtility
-import zope.container.btree
-
-from zope.app.authentication import interfaces
-
-class PluggableAuthentication(zope.container.btree.BTreeContainer):
-
- zope.interface.implements(
- IAuthentication,
- interfaces.IPluggableAuthentication,
- ISourceQueriables)
-
- authenticatorPlugins = ()
- credentialsPlugins = ()
-
- def __init__(self, prefix=''):
- super(PluggableAuthentication, self).__init__()
- self.prefix = prefix
-
- @property
- def registrationManager(self):
- import warnings
- warnings.warn(
- "The registration manager is deprecated and will go away in "
- "Zope 3.5. Use the simpler registration api (registerUtility) "
- "instead.",
- DeprecationWarning, 2)
- return self.__parent__.registrationManager
-
- def _plugins(self, names, interface):
- for name in names:
- plugin = self.get(name)
- if not interface.providedBy(plugin):
- plugin = component.queryUtility(interface, name, context=self)
- if plugin is not None:
- yield name, plugin
-
- def getAuthenticatorPlugins(self):
- return self._plugins(
- self.authenticatorPlugins, interfaces.IAuthenticatorPlugin)
-
- def getCredentialsPlugins(self):
- return self._plugins(
- self.credentialsPlugins, interfaces.ICredentialsPlugin)
-
- def authenticate(self, request):
- authenticatorPlugins = [p for n, p in self.getAuthenticatorPlugins()]
- for name, credplugin in self.getCredentialsPlugins():
- credentials = credplugin.extractCredentials(request)
- for authplugin in authenticatorPlugins:
- if authplugin is None:
- continue
- info = authplugin.authenticateCredentials(credentials)
- if info is None:
- continue
- info.credentialsPlugin = credplugin
- info.authenticatorPlugin = authplugin
- principal = component.getMultiAdapter((info, request),
- interfaces.IAuthenticatedPrincipalFactory)(self)
- principal.id = self.prefix + info.id
- return principal
- return None
-
- def getPrincipal(self, id):
- if not id.startswith(self.prefix):
- next = queryNextUtility(self, IAuthentication)
- if next is None:
- raise PrincipalLookupError(id)
- return next.getPrincipal(id)
- id = id[len(self.prefix):]
- for name, authplugin in self.getAuthenticatorPlugins():
- info = authplugin.principalInfo(id)
- if info is None:
- continue
- info.credentialsPlugin = None
- info.authenticatorPlugin = authplugin
- principal = interfaces.IFoundPrincipalFactory(info)(self)
- principal.id = self.prefix + info.id
- return principal
- next = queryNextUtility(self, IAuthentication)
- if next is not None:
- return next.getPrincipal(self.prefix + id)
- raise PrincipalLookupError(id)
-
- def getQueriables(self):
- for name, authplugin in self.getAuthenticatorPlugins():
- queriable = component.queryMultiAdapter((authplugin, self),
- interfaces.IQueriableAuthenticator)
- if queriable is not None:
- yield name, queriable
-
- def unauthenticatedPrincipal(self):
- return None
-
- def unauthorized(self, id, request):
- challengeProtocol = None
-
- for name, credplugin in self.getCredentialsPlugins():
- protocol = getattr(credplugin, 'challengeProtocol', None)
- if challengeProtocol is None or protocol == challengeProtocol:
- if credplugin.challenge(request):
- if protocol is None:
- return
- elif challengeProtocol is None:
- challengeProtocol = protocol
-
- if challengeProtocol is None:
- next = queryNextUtility(self, IAuthentication)
- if next is not None:
- next.unauthorized(id, request)
-
- def logout(self, request):
- challengeProtocol = None
-
- for name, credplugin in self.getCredentialsPlugins():
- protocol = getattr(credplugin, 'challengeProtocol', None)
- if challengeProtocol is None or protocol == challengeProtocol:
- if credplugin.logout(request):
- if protocol is None:
- return
- elif challengeProtocol is None:
- challengeProtocol = protocol
-
- if challengeProtocol is None:
- next = queryNextUtility(self, IAuthentication)
- if next is not None:
- next.logout(request)
-
-
-class QuerySchemaSearchAdapter(object):
- """Performs schema-based principal searches on behalf of a PAU.
-
- Delegates the search to the adapted authenticator (which also provides
- IQuerySchemaSearch) and prepends the PAU prefix to the resulting principal
- IDs.
- """
- component.adapts(
- interfaces.IQuerySchemaSearch,
- interfaces.IPluggableAuthentication)
-
- zope.interface.implements(
- interfaces.IQueriableAuthenticator,
- interfaces.IQuerySchemaSearch,
- ILocation)
-
- def __init__(self, authplugin, pau):
- 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:
- self.__parent__ = pau
- self.__name__ = ""
- self.authplugin = authplugin
- self.pau = pau
- self.schema = authplugin.schema
-
- def search(self, query, start=None, batch_size=None):
- for id in self.authplugin.search(query, start, batch_size):
- yield self.pau.prefix + id
Deleted: zope.password/trunk/src/zope/password/configure.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/configure.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/configure.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,69 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:browser="http://namespaces.zope.org/browser"
- i18n_domain="zope"
- >
-
- <class class=".PluggableAuthentication">
- <implements
- interface="zope.annotation.interfaces.IAttributeAnnotatable"
- />
- <require
- permission="zope.ManageSite"
- interface=".interfaces.IPluggableAuthentication"
- set_schema=".interfaces.IPluggableAuthentication"
- />
- <require
- permission="zope.ManageServices"
- attributes="registrationManager"
- />
- </class>
-
- <adapter
- for=".interfaces.IQuerySchemaSearch
- .interfaces.IPluggableAuthentication"
- provides=".interfaces.IQueriableAuthenticator"
- factory=".authentication.QuerySchemaSearchAdapter"
- />
-
- <utility
- component=".vocabulary.credentialsPlugins"
- name="CredentialsPlugins"
- />
-
- <utility
- component=".vocabulary.authenticatorPlugins"
- name="AuthenticatorPlugins"
- />
-
- <utility
- name="No Challenge if Authenticated"
- factory=".generic.NoChallengeCredentialsPlugin"
- provides=".interfaces.ICredentialsPlugin"
- />
-
- <!-- Registering documentation with API doc -->
- <configure
- xmlns:apidoc="http://namespaces.zope.org/apidoc"
- xmlns:zcml="http://namespaces.zope.org/zcml"
- zcml:condition="have apidoc">
-
- <apidoc:bookchapter
- id="authentication"
- title="Pluggable Authentication"
- doc_path="README.txt"
- parent="security"
- />
-
- </configure>
-
- <include file="password.zcml" />
- <include file="session.zcml" />
- <include file="httpplugins.zcml" />
- <include file="principalfolder.zcml" />
- <include file="groupfolder.zcml" />
- <include file="ftpplugins.zcml" />
-
- <include package=".browser" />
-
-</configure>
Copied: zope.password/trunk/src/zope/password/configure.zcml (from rev 97555, zope.app.authentication/trunk/src/zope/app/authentication/password.zcml)
===================================================================
--- zope.password/trunk/src/zope/password/configure.zcml (rev 0)
+++ zope.password/trunk/src/zope/password/configure.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -0,0 +1,39 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <class class=".password.PlainTextPasswordManager">
+ <allow interface=".interfaces.IPasswordManager" />
+ </class>
+
+ <utility
+ name="Plain Text"
+ provides=".interfaces.IPasswordManager"
+ factory=".password.PlainTextPasswordManager"
+ />
+
+ <class class=".password.MD5PasswordManager">
+ <allow interface=".interfaces.IPasswordManager" />
+ </class>
+
+ <utility
+ name="MD5"
+ provides=".interfaces.IPasswordManager"
+ factory=".password.MD5PasswordManager"
+ />
+
+ <class class=".password.SHA1PasswordManager">
+ <allow interface=".interfaces.IPasswordManager" />
+ </class>
+
+ <utility
+ name="SHA1"
+ provides=".interfaces.IPasswordManager"
+ factory=".password.SHA1PasswordManager"
+ />
+
+ <utility
+ name="SSHA"
+ provides=".interfaces.IPasswordManager"
+ factory=".password.SSHAPasswordManager"
+ />
+
+</configure>
Deleted: zope.password/trunk/src/zope/password/ftesting.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/ftesting.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/ftesting.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,70 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="zope"
- package="zope.app.authentication"
- >
-
- <!-- This file is the equivalent of site.zcml and it is -->
- <!-- used for functional testing setup -->
-
- <include package="zope.app.securitypolicy" file="meta.zcml" />
-
- <include package="zope.app.zcmlfiles" />
- <include package="zope.formlib" />
- <include package="zope.session" />
- <include package="zope.app.authentication" />
- <include package="zope.app.securitypolicy" />
-
- <securityPolicy
- component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
-
- <role id="zope.Anonymous" title="Everybody"
- description="All users have this role implicitly" />
- <role id="zope.Manager" title="Site Manager" />
-
- <!-- Replace the following directive if you don't want public access -->
- <grant permission="zope.View"
- role="zope.Anonymous" />
-
- <grantAll role="zope.Manager" />
-
- <include package="zope.app.securitypolicy.tests" file="functional.zcml" />
-
- <!-- Principals -->
-
- <unauthenticatedPrincipal
- id="zope.anybody"
- title="Unauthenticated User" />
-
- <unauthenticatedGroup
- id="zope.Anybody"
- title="Unauthenticated Users"
- />
-
- <authenticatedGroup
- id="zope.Authenticated"
- title="Authenticated Users"
- />
-
- <everybodyGroup
- id="zope.Everybody"
- title="All Users"
- />
-
- <!-- Principal that tests generally run as -->
- <principal
- id="zope.mgr"
- title="Manager"
- login="mgr"
- password="mgrpw" />
-
- <!-- Bootstrap principal used to make local grant to the principal above -->
- <principal
- id="zope.globalmgr"
- title="Manager"
- login="globalmgr"
- password="globalmgrpw" />
-
- <grant role="zope.Manager" principal="zope.globalmgr" />
-
-</configure>
Deleted: zope.password/trunk/src/zope/password/ftpplugins.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/ftpplugins.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/ftpplugins.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,65 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""PAS plugins related to FTP
-"""
-__docformat__ = 'restructuredtext'
-
-from zope.interface import implements
-from zope.publisher.interfaces.ftp import IFTPRequest
-
-from zope.app.authentication import interfaces
-
-class FTPCredentialsPlugin(object):
-
- implements(interfaces.ICredentialsPlugin)
-
- def extractCredentials(self, request):
- """Extracts the FTP credentials from a request.
-
- First we need to create a FTP request that contains some credentials.
- Note the path is a required in the envirnoment.
-
- >>> from zope.publisher.ftp import FTPRequest
- >>> from StringIO import StringIO
- >>> request = FTPRequest(StringIO(''),
- ... {'credentials': ('bob', '123'),
- ... 'path': '/a/b/c'})
-
- Now we create the plugin and get the credentials.
-
- >>> plugin = FTPCredentialsPlugin()
- >>> plugin.extractCredentials(request)
- {'login': u'bob', 'password': u'123'}
-
- This only works for FTPRequests.
-
- >>> from zope.publisher.base import TestRequest
- >>> print plugin.extractCredentials(TestRequest('/'))
- None
-
- """
- if not IFTPRequest.providedBy(request):
- return None
-
- if request._auth:
- login, password = request._auth
- return {'login': login.decode('utf-8'),
- 'password': password.decode('utf-8')}
- return None
-
- def challenge(self, request):
- return False
-
- def logout(self, request):
- return False
Deleted: zope.password/trunk/src/zope/password/ftpplugins.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/ftpplugins.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/ftpplugins.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,12 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="zope"
- >
-
- <utility
- name="FTP Credentials"
- provides=".interfaces.ICredentialsPlugin"
- factory=".ftpplugins.FTPCredentialsPlugin"
- />
-
-</configure>
Deleted: zope.password/trunk/src/zope/password/generic.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/generic.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/generic.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,95 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Generic PAS Plugins
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-from zope.interface import implements
-
-from zope.app.security.interfaces import IUnauthenticatedPrincipal
-
-from zope.app.authentication import interfaces
-
-
-class NoChallengeCredentialsPlugin(object):
- """A plugin that doesn't challenge if the principal is authenticated.
-
- There are two reasonable ways to handle an unauthorized error for an
- authenticated principal:
-
- - Inform the user of the unauthorized error
-
- - Let the user login with a different set of credentials
-
- Since either approach is reasonable, we need to give the site manager
- some way of specifying one of the two policies.
-
- By default, a user will be challenged for a new set of credentials if
- unauthorized. A site manager can insert this plugin in the front of the
- plugin list to prevent that challenge from occurring. This will
- typically result in an 'Unauthorized' message to the user.
-
- The 'challenge' behavior of the plugin is simple. To illustrate, we'll
- create a plugin:
-
- >>> challenger = NoChallengeCredentialsPlugin()
-
- and a test request with an authenticated principal:
-
- >>> from zope.publisher.browser import TestRequest
- >>> request = TestRequest()
- >>> IUnauthenticatedPrincipal.providedBy(request.principal)
- False
-
- When we challenge using the plugin:
-
- >>> challenger.challenge(request)
- True
-
- we get a value that signals the PAU that this plugin successfully
- challenged the user (even though it actually did nothing). The PAU
- will stop trying to challenge and the user will not get a chance to
- provide different credentials. The result is typically an error message.
-
- On the other hand, if the user is unauthenticated:
-
- >>> class Principal(object):
- ... implements(IUnauthenticatedPrincipal)
- >>> request.setPrincipal(Principal())
- >>> IUnauthenticatedPrincipal.providedBy(request.principal)
- True
-
- the plugin challenge will return None:
-
- >>> print challenger.challenge(request)
- None
-
- signaling the PAU that it should try the next plugin for a challenge. If
- the PAU is configured properly, the user will receive a challenge and be
- allowed to provide different credentials.
- """
- implements(interfaces.ICredentialsPlugin)
-
- def extractCredentials(self, request):
- return None
-
- def challenge(self, request):
- if not IUnauthenticatedPrincipal.providedBy(request.principal):
- return True
- return None
-
- def logout(self, request):
- return False
-
Deleted: zope.password/trunk/src/zope/password/groupfolder.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/groupfolder.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/groupfolder.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,363 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Zope Groups Folder implementation
-
-$Id$
-
-"""
-import BTrees.OOBTree
-import persistent
-
-from zope import interface, event, schema, component
-from zope.interface import alsoProvides
-from zope.security.interfaces import (
- IGroup, IGroupAwarePrincipal, IMemberAwareGroup)
-
-from zope.container.btree import BTreeContainer
-import zope.container.constraints
-import zope.container.interfaces
-from zope.app.authentication.i18n import ZopeMessageFactory as _
-import zope.app.security.vocabulary
-from zope.app.security.interfaces import (
- IAuthentication, IAuthenticatedGroup, IEveryoneGroup)
-from zope.app.authentication import principalfolder, interfaces
-
-
-class IGroupInformation(interface.Interface):
-
- title = schema.TextLine(
- title=_("Title"),
- description=_("Provides a title for the permission."),
- required=True)
-
- description = schema.Text(
- title=_("Description"),
- description=_("Provides a description for the permission."),
- required=False)
-
- principals = schema.List(
- title=_("Principals"),
- value_type=schema.Choice(
- source=zope.app.security.vocabulary.PrincipalSource()),
- description=_(
- "List of ids of principals which belong to the group"),
- required=False)
-
-
-class IGroupFolder(zope.container.interfaces.IContainer):
-
- zope.container.constraints.contains(IGroupInformation)
-
- prefix = schema.TextLine(
- title=_("Group ID prefix"),
- description=_("Prefix added to IDs of groups in this folder"),
- readonly=True,
- )
-
- def getGroupsForPrincipal(principalid):
- """Get groups the given principal belongs to"""
-
- def getPrincipalsForGroup(groupid):
- """Get principals which belong to the group"""
-
-
-class IGroupContained(zope.container.interfaces.IContained):
-
- zope.container.constraints.containers(IGroupFolder)
-
-class IGroupSearchCriteria(interface.Interface):
-
- search = schema.TextLine(
- title=_("Group Search String"),
- required=False,
- missing_value=u'',
- )
-
-class IGroupPrincipalInfo(interfaces.IPrincipalInfo):
- members = interface.Attribute('an iterable of members of the group')
-
-class GroupInfo(object):
- """An implementation of IPrincipalInfo used by the group folder.
-
- A group info is created with id, title, and description:
-
- >>> class DemoGroupInformation(object):
- ... interface.implements(IGroupInformation)
- ... def __init__(self, title, description, principals):
- ... self.title = title
- ... self.description = description
- ... self.principals = principals
- ...
- >>> i = DemoGroupInformation(
- ... 'Managers', 'Taskmasters', ('joe', 'jane'))
- ...
- >>> info = GroupInfo('groups.managers', i)
- >>> info
- GroupInfo('groups.managers')
- >>> info.id
- 'groups.managers'
- >>> info.title
- 'Managers'
- >>> info.description
- 'Taskmasters'
- >>> info.members
- ('joe', 'jane')
- >>> info.members = ('joe', 'jane', 'jaime')
- >>> info.members
- ('joe', 'jane', 'jaime')
-
- """
- interface.implements(IGroupPrincipalInfo)
-
- def __init__(self, id, information):
- self.id = id
- self._information = information
-
- @property
- def title(self):
- return self._information.title
-
- @property
- def description(self):
- return self._information.description
-
- @apply
- def members():
- def get(self):
- return self._information.principals
- def set(self, value):
- self._information.principals = value
- return property(get, set)
-
- def __repr__(self):
- return 'GroupInfo(%r)' % self.id
-
-
-class GroupFolder(BTreeContainer):
-
- interface.implements(
- interfaces.IAuthenticatorPlugin,
- interfaces.IQuerySchemaSearch,
- IGroupFolder)
-
- schema = IGroupSearchCriteria
-
- def __init__(self, prefix=u''):
- super(GroupFolder, self).__init__()
- self.prefix = prefix
- # __inversemapping is used to map principals to groups
- self.__inverseMapping = BTrees.OOBTree.OOBTree()
-
- def __setitem__(self, name, value):
- BTreeContainer.__setitem__(self, name, value)
- group_id = self._groupid(value)
- self._addPrincipalsToGroup(value.principals, group_id)
- if value.principals:
- event.notify(
- interfaces.PrincipalsAddedToGroup(
- value.principals, self.__parent__.prefix + group_id))
- group = principalfolder.Principal(self.prefix + name)
- event.notify(interfaces.GroupAdded(group))
-
- def __delitem__(self, name):
- value = self[name]
- group_id = self._groupid(value)
- self._removePrincipalsFromGroup(value.principals, group_id)
- if value.principals:
- event.notify(
- interfaces.PrincipalsRemovedFromGroup(
- value.principals, self.__parent__.prefix + group_id))
- BTreeContainer.__delitem__(self, name)
-
- def _groupid(self, group):
- return self.prefix+group.__name__
-
- def _addPrincipalsToGroup(self, principal_ids, group_id):
- for principal_id in principal_ids:
- self.__inverseMapping[principal_id] = (
- self.__inverseMapping.get(principal_id, ())
- + (group_id,))
-
- def _removePrincipalsFromGroup(self, principal_ids, group_id):
- for principal_id in principal_ids:
- groups = self.__inverseMapping.get(principal_id)
- if groups is None:
- return
- new = tuple([id for id in groups if id != group_id])
- if new:
- self.__inverseMapping[principal_id] = new
- else:
- del self.__inverseMapping[principal_id]
-
- def getGroupsForPrincipal(self, principalid):
- """Get groups the given principal belongs to"""
- return self.__inverseMapping.get(principalid, ())
-
- def getPrincipalsForGroup(self, groupid):
- """Get principals which belong to the group"""
- return self[groupid].principals
-
- def search(self, query, start=None, batch_size=None):
- """ Search for groups"""
- search = query.get('search')
- if search is not None:
- n = 0
- search = search.lower()
- for i, (id, groupinfo) in enumerate(self.items()):
- if (search in groupinfo.title.lower() or
- (groupinfo.description and
- search in groupinfo.description.lower())):
- if not ((start is not None and i < start)
- or
- (batch_size is not None and n >= batch_size)):
- n += 1
- yield self.prefix + id
-
- def authenticateCredentials(self, credentials):
- # user folders don't authenticate
- pass
-
- def principalInfo(self, id):
- if id.startswith(self.prefix):
- id = id[len(self.prefix):]
- info = self.get(id)
- if info is not None:
- return GroupInfo(
- self.prefix+id, info)
-
-class GroupCycle(Exception):
- """There is a cyclic relationship among groups
- """
-
-class InvalidPrincipalIds(Exception):
- """A user has a group id for a group that can't be found
- """
-
-class InvalidGroupId(Exception):
- """A user has a group id for a group that can't be found
- """
-
-def nocycles(principal_ids, seen, getPrincipal):
- for principal_id in principal_ids:
- if principal_id in seen:
- raise GroupCycle(principal_id, seen)
- seen.append(principal_id)
- principal = getPrincipal(principal_id)
- nocycles(principal.groups, seen, getPrincipal)
- seen.pop()
-
-class GroupInformation(persistent.Persistent):
-
- interface.implements(IGroupInformation, IGroupContained)
-
- __parent__ = __name__ = None
-
- _principals = ()
-
- def __init__(self, title='', description=''):
- self.title = title
- self.description = description
-
- def setPrincipals(self, prinlist, check=True):
- # method is not a part of the interface
- parent = self.__parent__
- old = self._principals
- self._principals = tuple(prinlist)
-
- if parent is not None:
- oldset = set(old)
- new = set(prinlist)
- group_id = parent._groupid(self)
- removed = oldset - new
- added = new - oldset
- try:
- parent._removePrincipalsFromGroup(removed, group_id)
- except AttributeError:
- removed = None
-
- try:
- parent._addPrincipalsToGroup(added, group_id)
- except AttributeError:
- added = None
-
- if check:
- try:
- principalsUtility = component.getUtility(IAuthentication)
- nocycles(new, [], principalsUtility.getPrincipal)
- except GroupCycle:
- # abort
- self.setPrincipals(old, False)
- raise
- # now that we've gotten past the checks, fire the events.
- if removed:
- event.notify(
- interfaces.PrincipalsRemovedFromGroup(
- removed, self.__parent__.__parent__.prefix + group_id))
- if added:
- event.notify(
- interfaces.PrincipalsAddedToGroup(
- added, self.__parent__.__parent__.prefix + group_id))
-
- principals = property(lambda self: self._principals, setPrincipals)
-
-
-def specialGroups(event):
- principal = event.principal
- if (IGroup.providedBy(principal) or
- not IGroupAwarePrincipal.providedBy(principal)):
- return
-
- everyone = component.queryUtility(IEveryoneGroup)
- if everyone is not None:
- principal.groups.append(everyone.id)
-
- auth = component.queryUtility(IAuthenticatedGroup)
- if auth is not None:
- principal.groups.append(auth.id)
-
-
-def setGroupsForPrincipal(event):
- """Set group information when a principal is created"""
-
- principal = event.principal
- if not IGroupAwarePrincipal.providedBy(principal):
- return
-
- authentication = event.authentication
-
- for name, plugin in authentication.getAuthenticatorPlugins():
- if not IGroupFolder.providedBy(plugin):
- continue
- groupfolder = plugin
- principal.groups.extend(
- [authentication.prefix + id
- for id in groupfolder.getGroupsForPrincipal(principal.id)
- ])
- id = principal.id
- prefix = authentication.prefix + groupfolder.prefix
- if id.startswith(prefix) and id[len(prefix):] in groupfolder:
- alsoProvides(principal, IGroup)
-
- at component.adapter(interfaces.IFoundPrincipalCreated)
-def setMemberSubscriber(event):
- """adds `getMembers`, `setMembers` to groups made from IGroupPrincipalInfo.
- """
- info = event.info
- if IGroupPrincipalInfo.providedBy(info):
- principal = event.principal
- principal.getMembers = lambda : info.members
- def setMembers(value):
- info.members = value
- principal.setMembers = setMembers
- alsoProvides(principal, IMemberAwareGroup)
Deleted: zope.password/trunk/src/zope/password/groupfolder.txt
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/groupfolder.txt 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/groupfolder.txt 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,427 +0,0 @@
-=============
-Group Folders
-=============
-
-Group folders provide support for groups information stored in the ZODB. They
-are persistent, and must be contained within the PAUs that use them.
-
-Like other principals, groups are created when they are needed.
-
-Group folders contain group-information objects that contain group information.
-We create group information using the `GroupInformation` class:
-
- >>> import zope.app.authentication.groupfolder
- >>> g1 = zope.app.authentication.groupfolder.GroupInformation("Group 1")
-
- >>> groups = zope.app.authentication.groupfolder.GroupFolder('group.')
- >>> groups['g1'] = g1
-
-Note that when group-info is added, a GroupAdded event is generated:
-
- >>> from zope.app.authentication import interfaces
- >>> from zope.component.eventtesting import getEvents
- >>> getEvents(interfaces.IGroupAdded)
- [<GroupAdded 'group.g1'>]
-
-Groups are defined with respect to an authentication service. Groups
-must be accessible via an authentication service and can contain
-principals accessible via an authentication service.
-
-To illustrate the group interaction with the authentication service,
-we'll create a sample authentication service:
-
- >>> from zope import interface
- >>> from zope.app.security.interfaces import IAuthentication
- >>> from zope.app.security.interfaces import PrincipalLookupError
- >>> from zope.security.interfaces import IGroupAwarePrincipal
- >>> from zope.app.authentication.groupfolder import setGroupsForPrincipal
-
- >>> class Principal:
- ... interface.implements(IGroupAwarePrincipal)
- ... def __init__(self, id, title='', description=''):
- ... self.id, self.title, self.description = id, title, description
- ... self.groups = []
-
- >>> class PrincipalCreatedEvent:
- ... def __init__(self, authentication, principal):
- ... self.authentication = authentication
- ... self.principal = principal
-
- >>> from zope.app.authentication import principalfolder
-
- >>> class Principals:
- ...
- ... interface.implements(IAuthentication)
- ...
- ... def __init__(self, groups, prefix='auth.'):
- ... self.prefix = prefix
- ... self.principals = {
- ... 'p1': principalfolder.PrincipalInfo('p1', '', '', ''),
- ... 'p2': principalfolder.PrincipalInfo('p2', '', '', ''),
- ... 'p3': principalfolder.PrincipalInfo('p3', '', '', ''),
- ... 'p4': principalfolder.PrincipalInfo('p4', '', '', ''),
- ... }
- ... self.groups = groups
- ... groups.__parent__ = self
- ...
- ... def getAuthenticatorPlugins(self):
- ... return [('principals', self.principals), ('groups', self.groups)]
- ...
- ... def getPrincipal(self, id):
- ... if not id.startswith(self.prefix):
- ... raise PrincipalLookupError(id)
- ... id = id[len(self.prefix):]
- ... info = self.principals.get(id)
- ... if info is None:
- ... info = self.groups.principalInfo(id)
- ... if info is None:
- ... raise PrincipalLookupError(id)
- ... principal = Principal(self.prefix+info.id,
- ... info.title, info.description)
- ... setGroupsForPrincipal(PrincipalCreatedEvent(self, principal))
- ... return principal
-
-This class doesn't really implement the full `IAuthentication` interface, but
-it implements the `getPrincipal` method used by groups. It works very much
-like the pluggable authentication utility. It creates principals on demand. It
-calls `setGroupsForPrincipal`, which is normally called as an event subscriber,
-when principals are created. In order for `setGroupsForPrincipal` to find out
-group folder, we have to register it as a utility:
-
- >>> from zope.app.testing import ztapi
- >>> from zope.app.authentication.interfaces import IAuthenticatorPlugin
- >>> ztapi.provideUtility(IAuthenticatorPlugin, groups)
-
-We will create and register a new principals utility:
-
- >>> principals = Principals(groups)
- >>> ztapi.provideUtility(IAuthentication, principals)
-
-Now we can set the principals on the group:
-
- >>> g1.principals = ['auth.p1', 'auth.p2']
- >>> g1.principals
- ('auth.p1', 'auth.p2')
-
-Adding principals fires an event.
-
- >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
- <PrincipalsAddedToGroup ['auth.p1', 'auth.p2'] u'auth.group.g1'>
-
-We can now look up groups for the principals:
-
- >>> groups.getGroupsForPrincipal('auth.p1')
- (u'group.g1',)
-
-Note that the group id is a concatenation of the group-folder prefix
-and the name of the group-information object within the folder.
-
-If we delete a group:
-
- >>> del groups['g1']
-
-then the groups folder loses the group information for that group's
-principals:
-
- >>> groups.getGroupsForPrincipal('auth.p1')
- ()
-
-but the principal information on the group is unchanged:
-
- >>> g1.principals
- ('auth.p1', 'auth.p2')
-
-It also fires an event showing that the principals are removed from the group
-(g1 is group information, not a zope.security.interfaces.IGroup).
-
- >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
- <PrincipalsRemovedFromGroup ['auth.p1', 'auth.p2'] u'auth.group.g1'>
-
-Adding the group sets the folder principal information. Let's use a
-different group name:
-
- >>> groups['G1'] = g1
-
- >>> groups.getGroupsForPrincipal('auth.p1')
- (u'group.G1',)
-
-Here we see that the new name is reflected in the group information.
-
-An event is fired, as usual.
-
- >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
- <PrincipalsAddedToGroup ['auth.p1', 'auth.p2'] u'auth.group.G1'>
-
-In terms of member events (principals added and removed from groups), we have
-now seen that events are fired when a group information object is added and
-when it is removed from a group folder; and we have seen that events are fired
-when a principal is added to an already-registered group. Events are also
-fired when a principal is removed from an already-registered group. Let's
-quickly see some more examples.
-
- >>> g1.principals = ('auth.p1', 'auth.p3', 'auth.p4')
- >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
- <PrincipalsAddedToGroup ['auth.p3', 'auth.p4'] u'auth.group.G1'>
- >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
- <PrincipalsRemovedFromGroup ['auth.p2'] u'auth.group.G1'>
- >>> g1.principals = ('auth.p1', 'auth.p2')
- >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
- <PrincipalsAddedToGroup ['auth.p2'] u'auth.group.G1'>
- >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
- <PrincipalsRemovedFromGroup ['auth.p3', 'auth.p4'] u'auth.group.G1'>
-
-Groups can contain groups:
-
- >>> g2 = zope.app.authentication.groupfolder.GroupInformation("Group Two")
- >>> groups['G2'] = g2
- >>> g2.principals = ['auth.group.G1']
-
- >>> groups.getGroupsForPrincipal('auth.group.G1')
- (u'group.G2',)
-
- >>> old = getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
- >>> old
- <PrincipalsAddedToGroup ['auth.group.G1'] u'auth.group.G2'>
-
-Groups cannot contain cycles:
-
- >>> g1.principals = ('auth.p1', 'auth.p2', 'auth.group.G2')
- ... # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- GroupCycle: (u'auth.group.G1',
- ['auth.p2', u'auth.group.G1', u'auth.group.G2'])
-
-Trying to do so does not fire an event.
-
- >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1] is old
- True
-
-They need not be hierarchical:
-
- >>> ga = zope.app.authentication.groupfolder.GroupInformation("Group A")
- >>> groups['GA'] = ga
-
- >>> gb = zope.app.authentication.groupfolder.GroupInformation("Group B")
- >>> groups['GB'] = gb
- >>> gb.principals = ['auth.group.GA']
-
- >>> gc = zope.app.authentication.groupfolder.GroupInformation("Group C")
- >>> groups['GC'] = gc
- >>> gc.principals = ['auth.group.GA']
-
- >>> gd = zope.app.authentication.groupfolder.GroupInformation("Group D")
- >>> groups['GD'] = gd
- >>> gd.principals = ['auth.group.GA', 'auth.group.GB']
-
- >>> ga.principals = ['auth.p1']
-
-Group folders provide a very simple search interface. They perform
-simple string searches on group titles and descriptions.
-
- >>> list(groups.search({'search': 'grou'})) # doctest: +NORMALIZE_WHITESPACE
- [u'group.G1', u'group.G2',
- u'group.GA', u'group.GB', u'group.GC', u'group.GD']
-
- >>> list(groups.search({'search': 'two'}))
- [u'group.G2']
-
-They also support batching:
-
- >>> list(groups.search({'search': 'grou'}, 2, 3))
- [u'group.GA', u'group.GB', u'group.GC']
-
-
-If you don't supply a search key, no results will be returned:
-
- >>> list(groups.search({}))
- []
-
-Identifying groups
-------------------
-The function, `setGroupsForPrincipal`, is a subscriber to
-principal-creation events. It adds any group-folder-defined groups to
-users in those groups:
-
- >>> principal = principals.getPrincipal('auth.p1')
-
- >>> principal.groups
- [u'auth.group.G1', u'auth.group.GA']
-
-Of course, this applies to groups too:
-
- >>> principal = principals.getPrincipal('auth.group.G1')
- >>> principal.id
- 'auth.group.G1'
-
- >>> principal.groups
- [u'auth.group.G2']
-
-In addition to setting principal groups, the `setGroupsForPrincipal`
-function also declares the `IGroup` interface on groups:
-
- >>> [iface.__name__ for iface in interface.providedBy(principal)]
- ['IGroup', 'IGroupAwarePrincipal']
-
- >>> [iface.__name__
- ... for iface in interface.providedBy(principals.getPrincipal('auth.p1'))]
- ['IGroupAwarePrincipal']
-
-Special groups
---------------
-Two special groups, Authenticated, and Everyone may apply to users
-created by the pluggable-authentication utility. There is a
-subscriber, specialGroups, that will set these groups on any non-group
-principals if IAuthenticatedGroup, or IEveryoneGroup utilities are
-provided.
-
-Lets define a group-aware principal:
-
- >>> import zope.security.interfaces
- >>> class GroupAwarePrincipal(Principal):
- ... interface.implements(zope.security.interfaces.IGroupAwarePrincipal)
- ... def __init__(self, id):
- ... Principal.__init__(self, id)
- ... self.groups = []
-
-If we notify the subscriber with this principal, nothing will happen
-because the groups haven't been defined:
-
- >>> prin = GroupAwarePrincipal('x')
- >>> event = interfaces.FoundPrincipalCreated(42, prin, {})
- >>> zope.app.authentication.groupfolder.specialGroups(event)
- >>> prin.groups
- []
-
-Now, if we define the Everybody group:
-
- >>> import zope.app.security.interfaces
- >>> class EverybodyGroup(Principal):
- ... interface.implements(zope.app.security.interfaces.IEveryoneGroup)
-
- >>> everybody = EverybodyGroup('all')
- >>> ztapi.provideUtility(zope.app.security.interfaces.IEveryoneGroup,
- ... everybody)
-
-Then the group will be added to the principal:
-
- >>> zope.app.authentication.groupfolder.specialGroups(event)
- >>> prin.groups
- ['all']
-
-Similarly for the authenticated group:
-
- >>> class AuthenticatedGroup(Principal):
- ... interface.implements(
- ... zope.app.security.interfaces.IAuthenticatedGroup)
-
- >>> authenticated = AuthenticatedGroup('auth')
- >>> ztapi.provideUtility(zope.app.security.interfaces.IAuthenticatedGroup,
- ... authenticated)
-
-Then the group will be added to the principal:
-
- >>> prin.groups = []
- >>> zope.app.authentication.groupfolder.specialGroups(event)
- >>> prin.groups.sort()
- >>> prin.groups
- ['all', 'auth']
-
-These groups are only added to non-group principals:
-
- >>> prin.groups = []
- >>> interface.directlyProvides(prin, zope.security.interfaces.IGroup)
- >>> zope.app.authentication.groupfolder.specialGroups(event)
- >>> prin.groups
- []
-
-And they are only added to group aware principals:
-
- >>> class SolitaryPrincipal:
- ... interface.implements(zope.security.interfaces.IPrincipal)
- ... id = title = description = ''
-
- >>> event = interfaces.FoundPrincipalCreated(42, SolitaryPrincipal(), {})
- >>> zope.app.authentication.groupfolder.specialGroups(event)
- >>> prin.groups
- []
-
-Member-aware groups
--------------------
-The groupfolder includes a subscriber that gives group principals the
-zope.security.interfaces.IGroupAware interface and an implementation thereof.
-This allows groups to be able to get and set their members.
-
-Given an info object and a group...
-
- >>> class DemoGroupInformation(object):
- ... interface.implements(
- ... zope.app.authentication.groupfolder.IGroupInformation)
- ... def __init__(self, title, description, principals):
- ... self.title = title
- ... self.description = description
- ... self.principals = principals
- ...
- >>> i = DemoGroupInformation(
- ... 'Managers', 'Taskmasters', ('joe', 'jane'))
- ...
- >>> info = zope.app.authentication.groupfolder.GroupInfo(
- ... 'groups.managers', i)
- >>> class DummyGroup(object):
- ... interface.implements(IGroupAwarePrincipal)
- ... def __init__(self, id, title=u'', description=u''):
- ... self.id = id
- ... self.title = title
- ... self.description = description
- ... self.groups = []
- ...
- >>> principal = DummyGroup('foo')
- >>> zope.security.interfaces.IMemberAwareGroup.providedBy(principal)
- False
-
-...when you call the subscriber, it adds the two pseudo-methods to the
-principal and makes the principal provide the IMemberAwareGroup interface.
-
- >>> zope.app.authentication.groupfolder.setMemberSubscriber(
- ... interfaces.FoundPrincipalCreated(
- ... 'dummy auth (ignored)', principal, info))
- >>> principal.getMembers()
- ('joe', 'jane')
- >>> principal.setMembers(('joe', 'jane', 'jaimie'))
- >>> principal.getMembers()
- ('joe', 'jane', 'jaimie')
- >>> zope.security.interfaces.IMemberAwareGroup.providedBy(principal)
- True
-
-The two methods work with the value on the IGroupInformation object.
-
- >>> i.principals == principal.getMembers()
- True
-
-Limitation
-==========
-
-The current group-folder design has an important limitation!
-
-There is no point in assigning principals to a group
-from a group folder unless the principal is from the same pluggable
-authentication utility.
-
-o If a principal is from a higher authentication utility, the user
- will not get the group definition. Why? Because the principals
- group assignments are set when the principal is authenticated. At
- that point, the current site is the site containing the principal
- definition. Groups defined in lower sites will not be consulted,
-
-o It is impossible to assign users from lower authentication
- utilities because they can't be seen when managing the group,
- from the site containing the group.
-
-A better design might be to store user-role assignments independent of
-the group definitions and to look for assignments during (url)
-traversal. This could get quite complex though.
-
-While it is possible to have multiple authentication utilities long a
-URL path, it is generally better to stick to a simpler model in which
-there is only one authentication utility along a URL path (in addition
-to the global utility, which is used for bootstrapping purposes).
Deleted: zope.password/trunk/src/zope/password/groupfolder.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/groupfolder.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/groupfolder.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,66 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="zope"
- >
-
- <class class=".groupfolder.GroupInformation">
- <implements
- interface="zope.annotation.interfaces.IAttributeAnnotatable"
- />
- <require
- permission="zope.ManageServices"
- interface=".groupfolder.IGroupInformation
- .groupfolder.IGroupContained"
- set_schema=".groupfolder.IGroupInformation"
- />
- </class>
-
- <class class=".groupfolder.GroupFolder">
- <implements
- interface=".groupfolder.IGroupFolder"
- />
- <require
- permission="zope.ManageServices"
- interface="zope.container.interfaces.IContainer
- zope.container.interfaces.INameChooser"
- />
- </class>
-
- <adapter
- provides="zope.container.interfaces.INameChooser"
- for=".groupfolder.IGroupFolder"
- factory=".idpicker.IdPicker"
- />
-
- <subscriber
- for=".interfaces.IPrincipalCreated"
- handler=".groupfolder.specialGroups"
- />
-
- <subscriber
- for=".interfaces.IPrincipalCreated"
- handler=".groupfolder.setGroupsForPrincipal"
- />
-
- <subscriber
- handler=".groupfolder.setMemberSubscriber"
- />
-
- <include package=".browser" file="groupfolder.zcml" />
-
- <!-- Registering documentation with API doc -->
- <configure
- xmlns:apidoc="http://namespaces.zope.org/apidoc"
- xmlns:zcml="http://namespaces.zope.org/zcml"
- zcml:condition="have apidoc">
-
- <apidoc:bookchapter
- id="groupfolder"
- title="Group Folders"
- doc_path="groupfolder.txt"
- parent="security/authentication"
- />
-
- </configure>
-
-</configure>
Deleted: zope.password/trunk/src/zope/password/httpplugins.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/httpplugins.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/httpplugins.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,142 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""PAS plugins related to HTTP
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-import base64
-from persistent import Persistent
-from zope.interface import implements, Interface
-from zope.publisher.interfaces.http import IHTTPRequest
-from zope.schema import TextLine
-
-from zope.container.contained import Contained
-from zope.app.authentication import interfaces
-
-
-class IHTTPBasicAuthRealm(Interface):
- """HTTP Basic Auth Realm
-
- Represents the realm string that is used during basic HTTP authentication
- """
-
- realm = TextLine(title=u'Realm',
- description=u'HTTP Basic Authentication Realm',
- required=True,
- default=u'Zope')
-
-
-class HTTPBasicAuthCredentialsPlugin(Persistent, Contained):
-
- implements(interfaces.ICredentialsPlugin, IHTTPBasicAuthRealm)
-
- realm = 'Zope'
-
- protocol = 'http auth'
-
- def extractCredentials(self, request):
- """Extracts HTTP basic auth credentials from a request.
-
- First we need to create a request that contains some credentials.
-
- >>> from zope.publisher.browser import TestRequest
- >>> request = TestRequest(
- ... environ={'HTTP_AUTHORIZATION': u'Basic bWdyOm1ncnB3'})
-
- Now create the plugin and get the credentials.
-
- >>> plugin = HTTPBasicAuthCredentialsPlugin()
- >>> plugin.extractCredentials(request)
- {'login': u'mgr', 'password': u'mgrpw'}
-
- Make sure we return `None`, if no authentication header has been
- specified.
-
- >>> print plugin.extractCredentials(TestRequest())
- None
-
- Also, this plugin can *only* handle basic authentication.
-
- >>> request = TestRequest(environ={'HTTP_AUTHORIZATION': 'foo bar'})
- >>> print plugin.extractCredentials(TestRequest())
- None
-
- This plugin only works with HTTP requests.
-
- >>> from zope.publisher.base import TestRequest
- >>> print plugin.extractCredentials(TestRequest('/'))
- None
-
- """
- if not IHTTPRequest.providedBy(request):
- return None
-
- if request._auth:
- if request._auth.lower().startswith(u'basic '):
- credentials = request._auth.split()[-1]
- login, password = base64.decodestring(credentials).split(':')
- return {'login': login.decode('utf-8'),
- 'password': password.decode('utf-8')}
- return None
-
- def challenge(self, request):
- """Issues an HTTP basic auth challenge for credentials.
-
- The challenge is issued by setting the appropriate response headers.
- To illustrate, we'll create a plugin:
-
- >>> plugin = HTTPBasicAuthCredentialsPlugin()
-
- The plugin adds its challenge to the HTTP response.
-
- >>> from zope.publisher.browser import TestRequest
- >>> request = TestRequest()
- >>> response = request.response
- >>> plugin.challenge(request)
- True
- >>> response._status
- 401
- >>> response.getHeader('WWW-Authenticate', literal=True)
- 'basic realm="Zope"'
-
- Notice that the realm is quoted, as per RFC 2617.
-
- The plugin only works with HTTP requests.
-
- >>> from zope.publisher.base import TestRequest
- >>> request = TestRequest('/')
- >>> response = request.response
- >>> print plugin.challenge(request)
- False
-
- """
- if not IHTTPRequest.providedBy(request):
- return False
- request.response.setHeader("WWW-Authenticate",
- 'basic realm="%s"' % self.realm,
- literal=True)
- request.response.setStatus(401)
- return True
-
- def logout(self, request):
- """Always returns False as logout is not supported by basic auth.
-
- >>> plugin = HTTPBasicAuthCredentialsPlugin()
- >>> from zope.publisher.browser import TestRequest
- >>> plugin.logout(TestRequest())
- False
-
- """
- return False
Deleted: zope.password/trunk/src/zope/password/httpplugins.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/httpplugins.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/httpplugins.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,25 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="zope"
- >
-
- <utility
- name="Zope Realm Basic-Auth"
- provides=".interfaces.ICredentialsPlugin"
- factory=".httpplugins.HTTPBasicAuthCredentialsPlugin"
- />
-
- <class class=".httpplugins.HTTPBasicAuthCredentialsPlugin">
- <implements
- interface="zope.annotation.interfaces.IAttributeAnnotatable"
- />
- <require
- permission="zope.ManageServices"
- interface=".httpplugins.IHTTPBasicAuthRealm"
- set_schema=".httpplugins.IHTTPBasicAuthRealm"
- />
- </class>
-
- <include package=".browser" file="httpplugins.zcml" />
-
-</configure>
Deleted: zope.password/trunk/src/zope/password/i18n.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/i18n.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/i18n.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,22 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Customization of zope.i18n for the Zope application server
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-# import this as _ to create i18n messages in the zope domain
-from zope.i18nmessageid import MessageFactory
-ZopeMessageFactory = MessageFactory('zope')
Deleted: zope.password/trunk/src/zope/password/idpicker.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/idpicker.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/idpicker.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,109 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-#
-##############################################################################
-"""Helper base class that picks principal ids
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-import re
-from zope.exceptions.interfaces import UserError
-from zope.container.contained import NameChooser
-from zope.app.authentication.i18n import ZopeMessageFactory as _
-
-ok = re.compile('[!-~]+$').match
-class IdPicker(NameChooser):
- """Helper base class that picks principal ids.
-
- Add numbers to ids given by users to make them unique.
-
- The Id picker is a variation on the name chooser that picks numeric
- ids when no name is given.
-
- >>> from zope.app.authentication.idpicker import IdPicker
- >>> IdPicker({}).chooseName('', None)
- u'1'
-
- >>> IdPicker({'1': 1}).chooseName('', None)
- u'2'
-
- >>> IdPicker({'2': 1}).chooseName('', None)
- u'1'
-
- >>> IdPicker({'1': 1}).chooseName('bob', None)
- u'bob'
-
- >>> IdPicker({'bob': 1}).chooseName('bob', None)
- u'bob1'
-
- """
- def chooseName(self, name, object):
- i = 0
- name = unicode(name)
- orig = name
- while (not name) or (name in self.context):
- i += 1
- name = orig+str(i)
-
- self.checkName(name, object)
- return name
-
- def checkName(self, name, object):
- """Limit ids
-
- Ids can only contain printable, non-space, 7-bit ASCII strings:
-
- >>> from zope.app.authentication.idpicker import IdPicker
- >>> IdPicker({}).checkName(u'1', None)
- True
-
- >>> IdPicker({}).checkName(u'bob', None)
- True
-
- >>> IdPicker({}).checkName(u'bob\xfa', None)
- ... # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- UserError: Ids must contain only printable
- 7-bit non-space ASCII characters
-
- >>> IdPicker({}).checkName(u'big bob', None)
- ... # doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- UserError: Ids must contain only printable
- 7-bit non-space ASCII characters
-
- Ids also can't be over 100 characters long:
-
- >>> IdPicker({}).checkName(u'x' * 100, None)
- True
-
- >>> IdPicker({}).checkName(u'x' * 101, None)
- Traceback (most recent call last):
- ...
- UserError: Ids can't be more than 100 characters long.
-
- """
- NameChooser.checkName(self, name, object)
- if not ok(name):
- raise UserError(
- _("Ids must contain only printable 7-bit non-space"
- " ASCII characters")
- )
- if len(name) > 100:
- raise UserError(
- _("Ids can't be more than 100 characters long.")
- )
- return True
Modified: zope.password/trunk/src/zope/password/interfaces.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/interfaces.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/interfaces.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2004 Zope Corporation and Contributors.
+# Copyright (c) 2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -11,141 +11,12 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Pluggable Authentication Utility Interfaces
+"""Password manager interface
$Id$
"""
-__docformat__ = "reStructuredText"
-
import zope.interface
-import zope.schema
-import zope.security.interfaces
-from zope.app.authentication.i18n import ZopeMessageFactory as _
-from zope.app.security.interfaces import ILogout
-from zope.container.constraints import contains, containers
-from zope.container.interfaces import IContainer
-
-class IPlugin(zope.interface.Interface):
- """A plugin for a pluggable authentication component."""
-
-
-class IPluggableAuthentication(ILogout, IContainer):
- """Provides authentication services with the help of various plugins.
-
- IPluggableAuthentication implementations will also implement
- zope.app.security.interfaces.IAuthentication. The `authenticate` method
- of this interface in an IPluggableAuthentication should annotate the
- IPrincipalInfo with the credentials plugin and authentication plugin used.
- The `getPrincipal` method should annotate the IPrincipalInfo with the
- authentication plugin used.
- """
-
- contains(IPlugin)
-
- credentialsPlugins = zope.schema.List(
- title=_('Credentials Plugins'),
- description=_("""Used for extracting credentials.
- Names may be of ids of non-utility ICredentialsPlugins contained in
- the IPluggableAuthentication, or names of registered
- ICredentialsPlugins utilities. Contained non-utility ids mask
- utility names."""),
- value_type=zope.schema.Choice(vocabulary='CredentialsPlugins'),
- default=[],
- )
-
- authenticatorPlugins = zope.schema.List(
- title=_('Authenticator Plugins'),
- description=_("""Used for converting credentials to principals.
- Names may be of ids of non-utility IAuthenticatorPlugins contained in
- the IPluggableAuthentication, or names of registered
- IAuthenticatorPlugins utilities. Contained non-utility ids mask
- utility names."""),
- value_type=zope.schema.Choice(vocabulary='AuthenticatorPlugins'),
- default=[],
- )
-
- def getCredentialsPlugins():
- """Return iterable of (plugin name, actual credentials plugin) pairs.
- Looks up names in credentialsPlugins as contained ids of non-utility
- ICredentialsPlugins first, then as registered ICredentialsPlugin
- utilities. Names that do not resolve are ignored."""
-
- def getAuthenticatorPlugins():
- """Return iterable of (plugin name, actual authenticator plugin) pairs.
- Looks up names in authenticatorPlugins as contained ids of non-utility
- IAuthenticatorPlugins first, then as registered IAuthenticatorPlugin
- utilities. Names that do not resolve are ignored."""
-
- prefix = zope.schema.TextLine(
- title=_('Prefix'),
- default=u'',
- required=True,
- readonly=True,
- )
-
- def logout(request):
- """Performs a logout by delegating to its authenticator plugins."""
-
-
-class ICredentialsPlugin(IPlugin):
- """Handles credentials extraction and challenges per request."""
-
- containers(IPluggableAuthentication)
-
- challengeProtocol = zope.interface.Attribute(
- """A challenge protocol used by the plugin.
-
- If a credentials plugin works with other credentials pluggins, it
- and the other cooperating plugins should specify a common (non-None)
- protocol. If a plugin returns True from its challenge method, then
- other credentials plugins will be called only if they have the same
- protocol.
- """)
-
- def extractCredentials(request):
- """Ties to extract credentials from a request.
-
- A return value of None indicates that no credentials could be found.
- Any other return value is treated as valid credentials.
- """
-
- def challenge(request):
- """Possibly issues a challenge.
-
- This is typically done in a protocol-specific way.
-
- If a challenge was issued, return True, otherwise return False.
- """
-
- def logout(request):
- """Possibly logout.
-
- If a logout was performed, return True, otherwise return False.
- """
-
-class IAuthenticatorPlugin(IPlugin):
- """Authenticates a principal using credentials.
-
- An authenticator may also be responsible for providing information
- about and creating principals.
- """
- containers(IPluggableAuthentication)
-
- def authenticateCredentials(credentials):
- """Authenticates credentials.
-
- If the credentials can be authenticated, return an object that provides
- IPrincipalInfo. If the plugin cannot authenticate the credentials,
- returns None.
- """
-
- def principalInfo(id):
- """Returns an IPrincipalInfo object for the specified principal id.
-
- If the plugin cannot find information for the id, returns None.
- """
-
class IPasswordManager(zope.interface.Interface):
"""Password manager."""
@@ -154,196 +25,3 @@
def checkPassword(storedPassword, password):
"""Return whether the password coincide with the storedPassword."""
-
-class IPrincipalInfo(zope.interface.Interface):
- """Minimal information about a principal."""
-
- id = zope.interface.Attribute("The principal id.")
-
- title = zope.interface.Attribute("The principal title.")
-
- description = zope.interface.Attribute("A description of the principal.")
-
- credentialsPlugin = zope.interface.Attribute(
- """Plugin used to generate the credentials for this principal info.
-
- Optional. Should be set in IPluggableAuthentication.authenticate.
- """)
-
- authenticatorPlugin = zope.interface.Attribute(
- """Plugin used to authenticate the credentials for this principal info.
-
- Optional. Should be set in IPluggableAuthentication.authenticate and
- IPluggableAuthentication.getPrincipal.
- """)
-
-class IPrincipal(zope.security.interfaces.IGroupClosureAwarePrincipal):
-
- groups = zope.schema.List(
- title=_("Groups"),
- description=_(
- """ids of groups to which the principal directly belongs.
-
- Plugins may append to this list. Mutating the list only affects
- the life of the principal object, and does not persist (so
- persistently adding groups to a principal should be done by working
- with a plugin that mutates this list every time the principal is
- created, like the group folder in this package.)
- """),
- value_type=zope.schema.TextLine(),
- required=False)
-
-class IPrincipalFactory(zope.interface.Interface):
- """A principal factory."""
-
- def __call__(authentication):
- """Creates a principal.
-
- The authentication utility that called the factory is passed
- and should be included in the principal-created event.
- """
-
-
-class IFoundPrincipalFactory(IPrincipalFactory):
- """A found principal factory."""
-
-
-class IAuthenticatedPrincipalFactory(IPrincipalFactory):
- """An authenticated principal factory."""
-
-
-class IPrincipalCreated(zope.interface.Interface):
- """A principal has been created."""
-
- principal = zope.interface.Attribute("The principal that was created")
-
- authentication = zope.interface.Attribute(
- "The authentication utility that created the principal")
-
- info = zope.interface.Attribute("An object providing IPrincipalInfo.")
-
-
-class IAuthenticatedPrincipalCreated(IPrincipalCreated):
- """A principal has been created by way of an authentication operation."""
-
- request = zope.interface.Attribute(
- "The request the user was authenticated against")
-
-
-class AuthenticatedPrincipalCreated:
- """
- >>> from zope.interface.verify import verifyObject
- >>> event = AuthenticatedPrincipalCreated("authentication", "principal",
- ... "info", "request")
- >>> verifyObject(IAuthenticatedPrincipalCreated, event)
- True
- """
-
- zope.interface.implements(IAuthenticatedPrincipalCreated)
-
- def __init__(self, authentication, principal, info, request):
- self.authentication = authentication
- self.principal = principal
- self.info = info
- self.request = request
-
-
-class IFoundPrincipalCreated(IPrincipalCreated):
- """A principal has been created by way of a search operation."""
-
-
-class FoundPrincipalCreated:
- """
- >>> from zope.interface.verify import verifyObject
- >>> event = FoundPrincipalCreated("authentication", "principal",
- ... "info")
- >>> verifyObject(IFoundPrincipalCreated, event)
- True
- """
-
- zope.interface.implements(IFoundPrincipalCreated)
-
- def __init__(self, authentication, principal, info):
- self.authentication = authentication
- self.principal = principal
- self.info = info
-
-
-class IQueriableAuthenticator(zope.interface.Interface):
- """Indicates the authenticator provides a search UI for principals."""
-
-
-class IQuerySchemaSearch(zope.interface.Interface):
- """An interface for searching using schema-constrained input."""
-
- schema = zope.interface.Attribute("""
- The schema that constrains the input provided to the search method.
-
- A mapping of name/value pairs for each field in this schema is used
- as the query argument in the search method.
- """)
-
- def search(query, start=None, batch_size=None):
- """Returns an iteration of principal IDs matching the query.
-
- query is a mapping of name/value pairs for fields specified by the
- schema.
-
- If the start argument is provided, then it should be an
- integer and the given number of initial items should be
- skipped.
-
- If the batch_size argument is provided, then it should be an
- integer and no more than the given number of items should be
- returned.
- """
-
-class IGroupAdded(zope.interface.Interface):
- """A group has been added."""
-
- group = zope.interface.Attribute("""The group that was defined""")
-
-
-class GroupAdded:
- """
- >>> from zope.interface.verify import verifyObject
- >>> event = GroupAdded("group")
- >>> verifyObject(IGroupAdded, event)
- True
- """
-
- zope.interface.implements(IGroupAdded)
-
- def __init__(self, group):
- self.group = group
-
- def __repr__(self):
- return "<GroupAdded %r>" % self.group.id
-
-class IPrincipalsAddedToGroup(zope.interface.Interface):
- group_id = zope.interface.Attribute(
- 'the id of the group to which the principal was added')
- principal_ids = zope.interface.Attribute(
- 'an iterable of one or more ids of principals added')
-
-class IPrincipalsRemovedFromGroup(zope.interface.Interface):
- group_id = zope.interface.Attribute(
- 'the id of the group from which the principal was removed')
- principal_ids = zope.interface.Attribute(
- 'an iterable of one or more ids of principals removed')
-
-class AbstractMembersChanged(object):
-
- def __init__(self, principal_ids, group_id):
- self.principal_ids = principal_ids
- self.group_id = group_id
-
- def __repr__(self):
- return "<%s %r %r>" % (
- self.__class__.__name__, sorted(self.principal_ids), self.group_id)
-
-class PrincipalsAddedToGroup(AbstractMembersChanged):
- zope.interface.implements(IPrincipalsAddedToGroup)
-
-class PrincipalsRemovedFromGroup(AbstractMembersChanged):
- zope.interface.implements(IPrincipalsRemovedFromGroup)
Modified: zope.password/trunk/src/zope/password/password.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/password.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/password.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2004 Zope Corporation and Contributors.
+# Copyright (c) 2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -17,6 +17,11 @@
"""
__docformat__ = 'restructuredtext'
+from base64 import urlsafe_b64encode
+from base64 import urlsafe_b64decode
+from os import urandom
+from random import randint
+from codecs import getencoder
try:
from hashlib import md5, sha1
except ImportError:
@@ -24,19 +29,9 @@
from md5 import new as md5
from sha import new as sha1
-from base64 import urlsafe_b64encode
-from base64 import urlsafe_b64decode
-from os import urandom
-from random import randint
-from codecs import getencoder
+from zope.interface import implements
+from zope.password.interfaces import IPasswordManager
-from zope.interface import implements, classProvides
-from zope.schema.interfaces import IVocabularyFactory
-from zope.app.component.vocabulary import UtilityVocabulary
-
-from zope.app.authentication.interfaces import IPasswordManager
-
-
_encoder = getencoder("utf-8")
@@ -68,30 +63,47 @@
return storedPassword == self.encodePassword(password)
-class MD5PasswordManager(PlainTextPasswordManager):
- """MD5 password manager.
+class SSHAPasswordManager(PlainTextPasswordManager):
+ """SSHA password manager.
- Note: use of salt in this password manager is purely
- cosmetical. Use SSHA if you want increased security.
+ SSHA is basically SHA1-encoding which also incorporates a salt
+ into the encoded string. This way, stored passwords are more
+ robust against dictionary attacks of attackers that could get
+ access to lists of encoded passwords.
+
+ SSHA is regularly used in LDAP databases and we should be
+ compatible with passwords used there.
>>> from zope.interface.verify import verifyObject
- >>> manager = MD5PasswordManager()
+ >>> manager = SSHAPasswordManager()
>>> verifyObject(IPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
>>> encoded = manager.encodePassword(password, salt="")
>>> encoded
- '{MD5}86dddccec45db4599f1ac00018e54139'
+ '{SSHA}BLTuxxVMXzouxtKVb7gLgNxzdAI='
+
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
False
+ Using the `slappasswd` utility to encode ``secret``, we get
+ ``{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv`` as seeded hash.
+
+ Our password manager generates the same value when seeded with the
+ same salt, so we can be sure, our output is compatible with
+ standard LDAP tools that also use SSHA::
+
+ >>> from base64 import urlsafe_b64decode
+ >>> salt = urlsafe_b64decode('XkOZbw==')
+ >>> encoded = manager.encodePassword('secret', salt)
+ >>> encoded
+ '{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv'
+
>>> encoded = manager.encodePassword(password)
- >>> encoded[-32:]
- '86dddccec45db4599f1ac00018e54139'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
@@ -105,41 +117,42 @@
def encodePassword(self, password, salt=None):
if salt is None:
- salt = "%08x" % randint(0, 0xffffffff)
- return '{MD5}%s%s' % (salt, md5(_encoder(password)[0]).hexdigest())
+ salt = urandom(4)
+ hash = sha1(_encoder(password)[0])
+ hash.update(salt)
+ return '{SSHA}' + urlsafe_b64encode(
+ hash.digest() + salt)
def checkPassword(self, storedPassword, password):
- if storedPassword.startswith('{MD5}'):
- salt = storedPassword[5:-32]
- return storedPassword == self.encodePassword(password, salt)
- salt = storedPassword[:-32]
- return storedPassword == self.encodePassword(password, salt)[5:]
+ byte_string = urlsafe_b64decode(storedPassword[6:])
+ salt = byte_string[20:]
+ return storedPassword == self.encodePassword(password, salt)
-class SHA1PasswordManager(PlainTextPasswordManager):
- """SHA1 password manager.
+class MD5PasswordManager(PlainTextPasswordManager):
+ """MD5 password manager.
Note: use of salt in this password manager is purely
cosmetical. Use SSHA if you want increased security.
>>> from zope.interface.verify import verifyObject
- >>> manager = SHA1PasswordManager()
+ >>> manager = MD5PasswordManager()
>>> verifyObject(IPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
>>> encoded = manager.encodePassword(password, salt="")
>>> encoded
- '{SHA1}04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
+ '{MD5}86dddccec45db4599f1ac00018e54139'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
False
>>> encoded = manager.encodePassword(password)
- >>> encoded[-40:]
- '04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
+ >>> encoded[-32:]
+ '86dddccec45db4599f1ac00018e54139'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
@@ -154,56 +167,40 @@
def encodePassword(self, password, salt=None):
if salt is None:
salt = "%08x" % randint(0, 0xffffffff)
- return '{SHA1}%s%s' % (salt, sha1(_encoder(password)[0]).hexdigest())
+ return '{MD5}%s%s' % (salt, md5(_encoder(password)[0]).hexdigest())
def checkPassword(self, storedPassword, password):
- if storedPassword.startswith('{SHA1}'):
- salt = storedPassword[6:-40]
+ if storedPassword.startswith('{MD5}'):
+ salt = storedPassword[5:-32]
return storedPassword == self.encodePassword(password, salt)
- salt = storedPassword[:-40]
- return storedPassword == self.encodePassword(password, salt)[6:]
+ salt = storedPassword[:-32]
+ return storedPassword == self.encodePassword(password, salt)[5:]
-class SSHAPasswordManager(PlainTextPasswordManager):
- """SSHA password manager.
- SSHA is basically SHA1-encoding which also incorporates a salt
- into the encoded string. This way, stored passwords are more
- robust against dictionary attacks of attackers that could get
- access to lists of encoded passwords.
+class SHA1PasswordManager(PlainTextPasswordManager):
+ """SHA1 password manager.
- SSHA is regularly used in LDAP databases and we should be
- compatible with passwords used there.
+ Note: use of salt in this password manager is purely
+ cosmetical. Use SSHA if you want increased security.
>>> from zope.interface.verify import verifyObject
- >>> manager = SSHAPasswordManager()
+ >>> manager = SHA1PasswordManager()
>>> verifyObject(IPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
>>> encoded = manager.encodePassword(password, salt="")
>>> encoded
- '{SSHA}BLTuxxVMXzouxtKVb7gLgNxzdAI='
-
+ '{SHA1}04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
False
- Using the `slappasswd` utility to encode ``secret``, we get
- ``{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv`` as seeded hash.
-
- Our password manager generates the same value when seeded with the
- same salt, so we can be sure, our output is compatible with
- standard LDAP tools that also use SSHA::
-
- >>> from base64 import urlsafe_b64decode
- >>> salt = urlsafe_b64decode('XkOZbw==')
- >>> encoded = manager.encodePassword('secret', salt)
- >>> encoded
- '{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv'
-
>>> encoded = manager.encodePassword(password)
+ >>> encoded[-40:]
+ '04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
@@ -217,29 +214,12 @@
def encodePassword(self, password, salt=None):
if salt is None:
- salt = urandom(4)
- hash = sha1(_encoder(password)[0])
- hash.update(salt)
- return '{SSHA}' + urlsafe_b64encode(
- hash.digest() + salt)
+ salt = "%08x" % randint(0, 0xffffffff)
+ return '{SHA1}%s%s' % (salt, sha1(_encoder(password)[0]).hexdigest())
def checkPassword(self, storedPassword, password):
- byte_string = urlsafe_b64decode(storedPassword[6:])
- salt = byte_string[20:]
- return storedPassword == self.encodePassword(password, salt)
-
-# Simple registry used by mkzopeinstance script
-managers = [
- ("Plain Text", PlainTextPasswordManager()), # default
- ("MD5", MD5PasswordManager()),
- ("SHA1", SHA1PasswordManager()),
- ("SSHA", SSHAPasswordManager()),
-]
-
-
-class PasswordManagerNamesVocabulary(UtilityVocabulary):
- """Vocabulary of password managers."""
-
- classProvides(IVocabularyFactory)
- interface = IPasswordManager
- nameOnly = True
+ if storedPassword.startswith('{SHA1}'):
+ salt = storedPassword[6:-40]
+ return storedPassword == self.encodePassword(password, salt)
+ salt = storedPassword[:-40]
+ return storedPassword == self.encodePassword(password, salt)[6:]
Deleted: zope.password/trunk/src/zope/password/password.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/password.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/password.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,47 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="zope"
- >
-
- <utility
- component=".password.PasswordManagerNamesVocabulary"
- name="Password Manager Names"
- />
-
- <class class=".password.PlainTextPasswordManager">
- <allow interface=".interfaces.IPasswordManager" />
- </class>
-
- <utility
- name="Plain Text"
- provides=".interfaces.IPasswordManager"
- factory=".password.PlainTextPasswordManager"
- />
-
- <class class=".password.MD5PasswordManager">
- <allow interface=".interfaces.IPasswordManager" />
- </class>
-
- <utility
- name="MD5"
- provides=".interfaces.IPasswordManager"
- factory=".password.MD5PasswordManager"
- />
-
- <class class=".password.SHA1PasswordManager">
- <allow interface=".interfaces.IPasswordManager" />
- </class>
-
- <utility
- name="SHA1"
- provides=".interfaces.IPasswordManager"
- factory=".password.SHA1PasswordManager"
- />
-
- <utility
- name="SSHA"
- provides=".interfaces.IPasswordManager"
- factory=".password.SSHAPasswordManager"
- />
-
-</configure>
Deleted: zope.password/trunk/src/zope/password/placelesssetup.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/placelesssetup.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/placelesssetup.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,35 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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 Placeless Setup
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-
-from zope.app.testing import ztapi
-from zope.app.authentication.interfaces import IPasswordManager
-from zope.app.authentication.password import PlainTextPasswordManager
-from zope.app.authentication.password import MD5PasswordManager
-from zope.app.authentication.password import SHA1PasswordManager
-from zope.app.authentication.password import SSHAPasswordManager
-
-
-class PlacelessSetup(object):
-
- def setUp(self):
- ztapi.provideUtility(IPasswordManager, PlainTextPasswordManager(),
- "Plain Text")
- ztapi.provideUtility(IPasswordManager, MD5PasswordManager(), "MD5")
- ztapi.provideUtility(IPasswordManager, SHA1PasswordManager(), "SHA1")
- ztapi.provideUtility(IPasswordManager, SSHAPasswordManager(), "SSHA")
Deleted: zope.password/trunk/src/zope/password/principalfolder.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/principalfolder.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/principalfolder.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,565 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""ZODB-based Authentication Source
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-
-from persistent import Persistent
-from zope import interface
-from zope import component
-from zope.event import notify
-from zope.schema import Text, TextLine, Password, Choice
-from zope.publisher.interfaces import IRequest
-
-from zope.container.interfaces import DuplicateIDError
-from zope.container.contained import Contained
-from zope.container.constraints import contains, containers
-from zope.container.btree import BTreeContainer
-from zope.app.authentication.i18n import ZopeMessageFactory as _
-from zope.app.security.interfaces import IAuthentication
-
-from zope.app.authentication import interfaces
-
-
-class IInternalPrincipal(interface.Interface):
- """Principal information"""
-
- login = TextLine(
- title=_("Login"),
- description=_("The Login/Username of the principal. "
- "This value can change."))
-
- def setPassword(password, passwordManagerName=None):
- pass
-
- password = Password(
- title=_("Password"),
- description=_("The password for the principal."))
-
- passwordManagerName = Choice(
- title=_("Password Manager"),
- vocabulary="Password Manager Names",
- description=_("The password manager will be used"
- " for encode/check the password"),
- default="SSHA",
- # TODO: The password manager name may be changed only
- # if the password changed
- readonly=True
- )
-
- title = TextLine(
- title=_("Title"),
- description=_("Provides a title for the principal."))
-
- description = Text(
- title=_("Description"),
- description=_("Provides a description for the principal."),
- required=False,
- missing_value='',
- default=u'')
-
-
-class IInternalPrincipalContainer(interface.Interface):
- """A container that contains internal principals."""
-
- prefix = TextLine(
- title=_("Prefix"),
- description=_(
- "Prefix to be added to all principal ids to assure "
- "that all ids are unique within the authentication service"),
- missing_value=u"",
- default=u'',
- readonly=True)
-
- def getIdByLogin(login):
- """Return the principal id currently associated with login.
-
- The return value includes the container prefix, but does not
- include the PAU prefix.
-
- KeyError is raised if no principal is associated with login.
-
- """
-
- contains(IInternalPrincipal)
-
-
-class IInternalPrincipalContained(interface.Interface):
- """Principal information"""
-
- containers(IInternalPrincipalContainer)
-
-
-class ISearchSchema(interface.Interface):
- """Search Interface for this Principal Provider"""
-
- search = TextLine(
- title=_("Search String"),
- description=_("A Search String"),
- required=False,
- default=u'',
- missing_value=u'')
-
-
-class InternalPrincipal(Persistent, Contained):
- """An internal principal for Persistent Principal Folder."""
-
- interface.implements(IInternalPrincipal, IInternalPrincipalContained)
-
- # If you're searching for self._passwordManagerName, or self._password
- # probably you just need to evolve the database to new generation
- # at /++etc++process/@@generations.html
-
- # NOTE: All changes needs to be synchronized with the evolver at
- # zope.app.zopeappgenerations.evolve2
-
- def __init__(self, login, password, title, description=u'',
- passwordManagerName="SSHA"):
- self._login = login
- self._passwordManagerName = passwordManagerName
- self.password = password
- self.title = title
- self.description = description
-
- def getPasswordManagerName(self):
- return self._passwordManagerName
-
- passwordManagerName = property(getPasswordManagerName)
-
- def _getPasswordManager(self):
- return component.getUtility(
- interfaces.IPasswordManager, self.passwordManagerName)
-
- def getPassword(self):
- return self._password
-
- def setPassword(self, password, passwordManagerName=None):
- if passwordManagerName is not None:
- self._passwordManagerName = passwordManagerName
- passwordManager = self._getPasswordManager()
- self._password = passwordManager.encodePassword(password)
-
- password = property(getPassword, setPassword)
-
- def checkPassword(self, password):
- passwordManager = self._getPasswordManager()
- return passwordManager.checkPassword(self.password, password)
-
- def getPassword(self):
- return self._password
-
- def getLogin(self):
- return self._login
-
- def setLogin(self, login):
- oldLogin = self._login
- self._login = login
- if self.__parent__ is not None:
- try:
- self.__parent__.notifyLoginChanged(oldLogin, self)
- except ValueError:
- self._login = oldLogin
- raise
-
- login = property(getLogin, setLogin)
-
-
-class PrincipalInfo(object):
- """An implementation of IPrincipalInfo used by the principal folder.
-
- A principal info is created with id, login, title, and description:
-
- >>> info = PrincipalInfo('users.foo', 'foo', 'Foo', 'An over-used term.')
- >>> info
- PrincipalInfo('users.foo')
- >>> info.id
- 'users.foo'
- >>> info.login
- 'foo'
- >>> info.title
- 'Foo'
- >>> info.description
- 'An over-used term.'
-
- """
- interface.implements(interfaces.IPrincipalInfo)
-
- def __init__(self, id, login, title, description):
- self.id = id
- self.login = login
- self.title = title
- self.description = description
-
- def __repr__(self):
- return 'PrincipalInfo(%r)' % self.id
-
-
-class PrincipalFolder(BTreeContainer):
- """A Persistent Principal Folder and Authentication plugin.
-
- See principalfolder.txt for details.
- """
-
- interface.implements(interfaces.IAuthenticatorPlugin,
- interfaces.IQuerySchemaSearch,
- IInternalPrincipalContainer)
-
- schema = ISearchSchema
-
- def __init__(self, prefix=''):
- self.prefix = unicode(prefix)
- super(PrincipalFolder, self).__init__()
- self.__id_by_login = self._newContainerData()
-
- def notifyLoginChanged(self, oldLogin, principal):
- """Notify the Container about changed login of a principal.
-
- We need this, so that our second tree can be kept up-to-date.
- """
- # A user with the new login already exists
- if principal.login in self.__id_by_login:
- raise ValueError('Principal Login already taken!')
-
- del self.__id_by_login[oldLogin]
- self.__id_by_login[principal.login] = principal.__name__
-
- def __setitem__(self, id, principal):
- """Add principal information.
-
- Create a Principal Folder
-
- >>> pf = PrincipalFolder()
-
- Create a principal with 1 as id
- Add a login attr since __setitem__ is in need of one
-
- >>> principal = Principal(1)
- >>> principal.login = 1
-
- Add the principal within the Principal Folder
-
- >>> pf.__setitem__(u'1', principal)
-
- Try to add another principal with the same id.
- It should raise a DuplicateIDError
-
- >>> try:
- ... pf.__setitem__(u'1', principal)
- ... except DuplicateIDError, e:
- ... pass
- >>>
- """
- # A user with the new login already exists
- if principal.login in self.__id_by_login:
- raise DuplicateIDError('Principal Login already taken!')
-
- super(PrincipalFolder, self).__setitem__(id, principal)
- self.__id_by_login[principal.login] = id
-
- def __delitem__(self, id):
- """Remove principal information."""
- principal = self[id]
- super(PrincipalFolder, self).__delitem__(id)
- del self.__id_by_login[principal.login]
-
- def authenticateCredentials(self, credentials):
- """Return principal info if credentials can be authenticated
- """
- if not isinstance(credentials, dict):
- return None
- if not ('login' in credentials and 'password' in credentials):
- return None
- id = self.__id_by_login.get(credentials['login'])
- if id is None:
- return None
- internal = self[id]
- if not internal.checkPassword(credentials["password"]):
- return None
- return PrincipalInfo(self.prefix + id, internal.login, internal.title,
- internal.description)
-
- def principalInfo(self, id):
- if id.startswith(self.prefix):
- internal = self.get(id[len(self.prefix):])
- if internal is not None:
- return PrincipalInfo(id, internal.login, internal.title,
- internal.description)
-
- def getIdByLogin(self, login):
- return self.prefix + self.__id_by_login[login]
-
- def search(self, query, start=None, batch_size=None):
- """Search through this principal provider."""
- search = query.get('search')
- if search is None:
- return
- search = search.lower()
- n = 1
- for i, value in enumerate(self.values()):
- if (search in value.title.lower() or
- search in value.description.lower() or
- search in value.login.lower()):
- if not ((start is not None and i < start)
- or (batch_size is not None and n > batch_size)):
- n += 1
- yield self.prefix + value.__name__
-
-class Principal(object):
- """A group-aware implementation of zope.security.interfaces.IPrincipal.
-
- A principal is created with an ID:
-
- >>> p = Principal(1)
- >>> p
- Principal(1)
- >>> p.id
- 1
-
- title and description may also be provided:
-
- >>> p = Principal('george', 'George', 'A site member.')
- >>> p
- Principal('george')
- >>> p.id
- 'george'
- >>> p.title
- 'George'
- >>> p.description
- 'A site member.'
-
- The `groups` is a simple list, filled in by plugins.
-
- >>> p.groups
- []
-
- The `allGroups` attribute is a readonly iterable of the full closure of the
- groups in the `groups` attribute--that is, if the principal is a direct
- member of the 'Administrators' group, and the 'Administrators' group is
- a member of the 'Reviewers' group, then p.groups would be
- ['Administrators'] and list(p.allGroups) would be
- ['Administrators', 'Reviewers'].
-
- To illustrate this, we'll need to set up a dummy authentication utility,
- and a few principals. Our main principal will also gain some groups, as if
- plugins had added the groups to the list. This is all setup--skip to the
- next block to actually see `allGroups` in action.
-
- >>> p.groups.extend(
- ... ['content_administrators', 'zope_3_project',
- ... 'list_administrators', 'zpug'])
- >>> editor = Principal('editors', 'Content Editors')
- >>> creator = Principal('creators', 'Content Creators')
- >>> reviewer = Principal('reviewers', 'Content Reviewers')
- >>> reviewer.groups.extend(['editors', 'creators'])
- >>> usermanager = Principal('user_managers', 'User Managers')
- >>> contentAdmin = Principal(
- ... 'content_administrators', 'Content Administrators')
- >>> contentAdmin.groups.extend(['reviewers', 'user_managers'])
- >>> zope3Dev = Principal('zope_3_project', 'Zope 3 Developer')
- >>> zope3ListAdmin = Principal(
- ... 'zope_3_list_admin', 'Zope 3 List Administrators')
- >>> zope3ListAdmin.groups.append('zope_3_project') # duplicate, but
- ... # should only appear in allGroups once
- >>> listAdmin = Principal('list_administrators', 'List Administrators')
- >>> listAdmin.groups.append('zope_3_list_admin')
- >>> zpugMember = Principal('zpug', 'ZPUG Member')
- >>> martians = Principal('martians', 'Martians') # not in p's allGroups
- >>> group_data = dict((p.id, p) for p in (
- ... editor, creator, reviewer, usermanager, contentAdmin,
- ... zope3Dev, zope3ListAdmin, listAdmin, zpugMember, martians))
- >>> class DemoAuth(object):
- ... interface.implements(IAuthentication)
- ... def getPrincipal(self, id):
- ... return group_data[id]
- ...
- >>> demoAuth = DemoAuth()
- >>> component.provideUtility(demoAuth)
-
- Now, we have a user with the following groups (lowest level are p's direct
- groups, and lines show membership):
-
- editors creators
- \------/
- | zope_3_project (duplicate)
- reviewers user_managers |
- \---------/ zope_3_list_admin
- | |
- content_administrators zope_3_project list_administrators zpug
-
- The allGroups value includes all of the shown groups, and with
- 'zope_3_project' only appearing once.
-
- >>> p.groups # doctest: +NORMALIZE_WHITESPACE
- ['content_administrators', 'zope_3_project', 'list_administrators',
- 'zpug']
- >>> list(p.allGroups) # doctest: +NORMALIZE_WHITESPACE
- ['content_administrators', 'reviewers', 'editors', 'creators',
- 'user_managers', 'zope_3_project', 'list_administrators',
- 'zope_3_list_admin', 'zpug']
- """
- interface.implements(interfaces.IPrincipal)
-
- def __init__(self, id, title=u'', description=u''):
- self.id = id
- self.title = title
- self.description = description
- self.groups = []
-
- def __repr__(self):
- return 'Principal(%r)' % self.id
-
- @property
- def allGroups(self):
- if self.groups:
- seen = set()
- principals = component.getUtility(IAuthentication)
- stack = [iter(self.groups)]
- while stack:
- try:
- group_id = stack[-1].next()
- except StopIteration:
- stack.pop()
- else:
- if group_id not in seen:
- yield group_id
- seen.add(group_id)
- group = principals.getPrincipal(group_id)
- stack.append(iter(group.groups))
-
-class AuthenticatedPrincipalFactory(object):
- """Creates 'authenticated' principals.
-
- An authenticated principal is created as a result of an authentication
- operation.
-
- To use the factory, create it with the info (interfaces.IPrincipalInfo) of
- the principal to create and a request:
-
- >>> info = PrincipalInfo('users.mary', 'mary', 'Mary', 'The site admin.')
- >>> from zope.publisher.base import TestRequest
- >>> request = TestRequest('/')
- >>> factory = AuthenticatedPrincipalFactory(info, request)
-
- The factory must be called with a pluggable-authentication object:
-
- >>> class Auth:
- ... prefix = 'auth.'
- >>> auth = Auth()
-
- >>> principal = factory(auth)
-
- The factory uses the pluggable authentication and the info to
- create a principal with the same ID, title, and description:
-
- >>> principal.id
- 'auth.users.mary'
- >>> principal.title
- 'Mary'
- >>> principal.description
- 'The site admin.'
-
- It also fires an AuthenticatedPrincipalCreatedEvent:
-
- >>> from zope.component.eventtesting import getEvents
- >>> [event] = getEvents(interfaces.IAuthenticatedPrincipalCreated)
- >>> event.principal is principal, event.authentication is auth
- (True, True)
- >>> event.info
- PrincipalInfo('users.mary')
- >>> event.request is request
- True
-
- Listeners can subscribe to this event to perform additional operations
- when the authenticated principal is created.
-
- For information on how factories are used in the authentication process,
- see README.txt.
- """
- component.adapts(interfaces.IPrincipalInfo, IRequest)
-
- interface.implements(interfaces.IAuthenticatedPrincipalFactory)
-
- def __init__(self, info, request):
- self.info = info
- self.request = request
-
- def __call__(self, authentication):
- principal = Principal(authentication.prefix + self.info.id,
- self.info.title,
- self.info.description)
- notify(interfaces.AuthenticatedPrincipalCreated(
- authentication, principal, self.info, self.request))
- return principal
-
-
-class FoundPrincipalFactory(object):
- """Creates 'found' principals.
-
- A 'found' principal is created as a result of a principal lookup.
-
- To use the factory, create it with the info (interfaces.IPrincipalInfo) of
- the principal to create:
-
- >>> info = PrincipalInfo('users.sam', 'sam', 'Sam', 'A site user.')
- >>> factory = FoundPrincipalFactory(info)
-
- The factory must be called with a pluggable-authentication object:
-
- >>> class Auth:
- ... prefix = 'auth.'
- >>> auth = Auth()
-
- >>> principal = factory(auth)
-
- The factory uses the pluggable-authentication object and the info
- to create a principal with the same ID, title, and description:
-
- >>> principal.id
- 'auth.users.sam'
- >>> principal.title
- 'Sam'
- >>> principal.description
- 'A site user.'
-
- It also fires a FoundPrincipalCreatedEvent:
-
- >>> from zope.component.eventtesting import getEvents
- >>> [event] = getEvents(interfaces.IFoundPrincipalCreated)
- >>> event.principal is principal, event.authentication is auth
- (True, True)
- >>> event.info
- PrincipalInfo('users.sam')
-
- Listeners can subscribe to this event to perform additional operations
- when the 'found' principal is created.
-
- For information on how factories are used in the authentication process,
- see README.txt.
- """
- component.adapts(interfaces.IPrincipalInfo)
-
- interface.implements(interfaces.IFoundPrincipalFactory)
-
- def __init__(self, info):
- self.info = info
-
- def __call__(self, authentication):
- principal = Principal(authentication.prefix + self.info.id,
- self.info.title,
- self.info.description)
- notify(interfaces.FoundPrincipalCreated(authentication,
- principal, self.info))
- return principal
Deleted: zope.password/trunk/src/zope/password/principalfolder.txt
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/principalfolder.txt 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/principalfolder.txt 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,170 +0,0 @@
-================
-Principal Folder
-================
-
-Principal folders contain principal-information objects that contain principal
-information. We create an internal principal using the `InternalPrincipal`
-class:
-
- >>> from zope.app.authentication.principalfolder import InternalPrincipal
- >>> p1 = InternalPrincipal('login1', '123', "Principal 1",
- ... passwordManagerName="SHA1")
- >>> p2 = InternalPrincipal('login2', '456', "The Other One")
-
-and add them to a principal folder:
-
- >>> from zope.app.authentication.principalfolder import PrincipalFolder
- >>> principals = PrincipalFolder('principal.')
- >>> principals['p1'] = p1
- >>> principals['p2'] = p2
-
-Authentication
---------------
-
-Principal folders provide the `IAuthenticatorPlugin` interface. When we
-provide suitable credentials:
-
- >>> from zope.testing.doctestunit import pprint
- >>> principals.authenticateCredentials({'login': 'login1', 'password': '123'})
- PrincipalInfo(u'principal.p1')
-
-We get back a principal id and supplementary information, including the
-principal title and description. Note that the principal id is a concatenation
-of the principal-folder prefix and the name of the principal-information object
-within the folder.
-
-None is returned if the credentials are invalid:
-
- >>> principals.authenticateCredentials({'login': 'login1',
- ... 'password': '1234'})
- >>> principals.authenticateCredentials(42)
-
-Search
-------
-
-Principal folders also provide the IQuerySchemaSearch interface. This
-supports both finding principal information based on their ids:
-
- >>> principals.principalInfo('principal.p1')
- PrincipalInfo('principal.p1')
-
- >>> principals.principalInfo('p1')
-
-and searching for principals based on a search string:
-
- >>> list(principals.search({'search': 'other'}))
- [u'principal.p2']
-
- >>> list(principals.search({'search': 'OTHER'}))
- [u'principal.p2']
-
- >>> list(principals.search({'search': ''}))
- [u'principal.p1', u'principal.p2']
-
- >>> list(principals.search({'search': 'eek'}))
- []
-
- >>> list(principals.search({}))
- []
-
-If there are a large number of matches:
-
- >>> for i in range(20):
- ... i = str(i)
- ... p = InternalPrincipal('l'+i, i, "Dude "+i)
- ... principals[i] = p
-
- >>> pprint(list(principals.search({'search': 'D'})))
- [u'principal.0',
- u'principal.1',
- u'principal.10',
- u'principal.11',
- u'principal.12',
- u'principal.13',
- u'principal.14',
- u'principal.15',
- u'principal.16',
- u'principal.17',
- u'principal.18',
- u'principal.19',
- u'principal.2',
- u'principal.3',
- u'principal.4',
- u'principal.5',
- u'principal.6',
- u'principal.7',
- u'principal.8',
- u'principal.9']
-
-We can use batching parameters to specify a subset of results:
-
- >>> pprint(list(principals.search({'search': 'D'}, start=17)))
- [u'principal.7',
- u'principal.8',
- u'principal.9']
-
- >>> pprint(list(principals.search({'search': 'D'}, batch_size=5)))
- [u'principal.0',
- u'principal.1',
- u'principal.10',
- u'principal.11',
- u'principal.12']
-
- >>> pprint(list(principals.search({'search': 'D'}, start=5, batch_size=5)))
- [u'principal.13',
- u'principal.14',
- u'principal.15',
- u'principal.16',
- u'principal.17']
-
-There is an additional method that allows requesting the principal id
-associated with a login id. The method raises KeyError when there is
-no associated principal::
-
- >>> principals.getIdByLogin("not-there")
- Traceback (most recent call last):
- KeyError: 'not-there'
-
-If there is a matching principal, the id is returned::
-
- >>> principals.getIdByLogin("login1")
- u'principal.p1'
-
-Changing credentials
---------------------
-
-Credentials can be changed by modifying principal-information objects:
-
- >>> p1.login = 'bob'
- >>> p1.password = 'eek'
-
- >>> principals.authenticateCredentials({'login': 'bob', 'password': 'eek'})
- PrincipalInfo(u'principal.p1')
-
- >>> principals.authenticateCredentials({'login': 'login1',
- ... 'password': 'eek'})
-
- >>> principals.authenticateCredentials({'login': 'bob',
- ... 'password': '123'})
-
-
-It is an error to try to pick a login name that is already taken:
-
- >>> p1.login = 'login2'
- Traceback (most recent call last):
- ...
- ValueError: Principal Login already taken!
-
-If such an attempt is made, the data are unchanged:
-
- >>> principals.authenticateCredentials({'login': 'bob', 'password': 'eek'})
- PrincipalInfo(u'principal.p1')
-
-Removing principals
--------------------
-
-Of course, if a principal is removed, we can no-longer authenticate it:
-
- >>> del principals['p1']
- >>> principals.authenticateCredentials({'login': 'bob',
- ... 'password': 'eek'})
Deleted: zope.password/trunk/src/zope/password/principalfolder.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/principalfolder.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/principalfolder.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,59 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="zope"
- >
-
- <class class=".principalfolder.InternalPrincipal">
- <require
- permission="zope.ManageServices"
- interface=".principalfolder.IInternalPrincipal"
- set_schema=".principalfolder.IInternalPrincipal"
- />
- </class>
-
- <class class=".principalfolder.PrincipalFolder">
-
- <implements
- interface="zope.annotation.interfaces.IAttributeAnnotatable"
- />
-
- <require
- permission="zope.ManageServices"
- interface="zope.container.interfaces.IContainer"
- />
-
- <require
- permission="zope.ManageServices"
- attributes="prefix"
- />
-
- </class>
-
- <adapter
- provides="zope.container.interfaces.INameChooser"
- for=".principalfolder.IInternalPrincipalContainer"
- factory=".idpicker.IdPicker"
- />
-
- <adapter factory=".principalfolder.FoundPrincipalFactory" />
-
- <adapter factory=".principalfolder.AuthenticatedPrincipalFactory" />
-
- <include package=".browser" file="principalfolder.zcml" />
-
- <!-- Registering documentation with API doc -->
- <configure
- xmlns:apidoc="http://namespaces.zope.org/apidoc"
- xmlns:zcml="http://namespaces.zope.org/zcml"
- zcml:condition="have apidoc">
-
- <apidoc:bookchapter
- id="principalfolder"
- title="Principal Folder"
- doc_path="principalfolder.txt"
- parent="security/authentication"
- />
-
- </configure>
-
-</configure>
Deleted: zope.password/trunk/src/zope/password/session.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/session.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/session.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,301 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-""" Implementations of the session-based and cookie-based extractor and
- challenge plugins.
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-import transaction
-from persistent import Persistent
-from urllib import urlencode
-
-from zope.interface import implements, Interface
-from zope.schema import TextLine
-from zope.publisher.interfaces.http import IHTTPRequest
-from zope.session.interfaces import ISession
-from zope.traversing.browser.absoluteurl import absoluteURL
-
-from zope.app.component import hooks
-from zope.container.contained import Contained
-from zope.app.authentication.interfaces import ICredentialsPlugin
-
-class ISessionCredentials(Interface):
- """ Interface for storing and accessing credentials in a session.
-
- We use a real class with interface here to prevent unauthorized
- access to the credentials.
- """
-
- def __init__(login, password):
- pass
-
- def getLogin():
- """Return login name."""
-
- def getPassword():
- """Return password."""
-
-
-class SessionCredentials(object):
- """Credentials class for use with sessions.
-
- A session credential is created with a login and a password:
-
- >>> cred = SessionCredentials('scott', 'tiger')
-
- Logins are read using getLogin:
- >>> cred.getLogin()
- 'scott'
-
- and passwords with getPassword:
-
- >>> cred.getPassword()
- 'tiger'
-
- """
- implements(ISessionCredentials)
-
- def __init__(self, login, password):
- self.login = login
- self.password = password
-
- def getLogin(self): return self.login
-
- def getPassword(self): return self.password
-
- def __str__(self): return self.getLogin() + ':' + self.getPassword()
-
-
-class IBrowserFormChallenger(Interface):
- """A challenger that uses a browser form to collect user credentials."""
-
- loginpagename = TextLine(
- title=u'Loginpagename',
- description=u"""Name of the login form used by challenger.
-
- The form must provide 'login' and 'password' input fields.
- """,
- default=u'loginForm.html')
-
- loginfield = TextLine(
- title=u'Loginfield',
- description=u"Field of the login page in which is looked for the login user name.",
- default=u"login")
-
- passwordfield = TextLine(
- title=u'Passwordfield',
- description=u"Field of the login page in which is looked for the password.",
- default=u"password")
-
-
-class SessionCredentialsPlugin(Persistent, Contained):
- """A credentials plugin that uses Zope sessions to get/store credentials.
-
- To illustrate how a session plugin works, we'll first setup some session
- machinery:
-
- >>> from zope.session.session import RAMSessionDataContainer
- >>> from tests import sessionSetUp
- >>> sessionSetUp(RAMSessionDataContainer)
-
- This lets us retrieve the same session info from any test request, which
- simulates what happens when a user submits a session ID as a cookie.
-
- We also need a session plugin:
-
- >>> plugin = SessionCredentialsPlugin()
-
- A session plugin uses an ISession component to store the last set of
- credentials it gets from a request. Credentials can be retrieved from
- subsequent requests using the session-stored credentials.
-
- Our test environment is initially configured without credentials:
-
- >>> from tests import sessionSetUp
- >>> from zope.publisher.browser import TestRequest
- >>> request = TestRequest()
- >>> print plugin.extractCredentials(request)
- None
-
- We must explicitly provide credentials once so the plugin can store
- them in a session:
-
- >>> request = TestRequest(login='scott', password='tiger')
- >>> plugin.extractCredentials(request)
- {'login': 'scott', 'password': 'tiger'}
-
- Subsequent requests now have access to the credentials even if they're
- not explicitly in the request:
-
- >>> plugin.extractCredentials(TestRequest())
- {'login': 'scott', 'password': 'tiger'}
-
- We can always provide new credentials explicitly in the request:
-
- >>> plugin.extractCredentials(TestRequest(
- ... login='harry', password='hirsch'))
- {'login': 'harry', 'password': 'hirsch'}
-
- and these will be used on subsequent requests:
-
- >>> plugin.extractCredentials(TestRequest())
- {'login': 'harry', 'password': 'hirsch'}
-
- We can also change the fields from which the credentials are extracted:
-
- >>> plugin.loginfield = "my_new_login_field"
- >>> plugin.passwordfield = "my_new_password_field"
-
- Now we build a request that uses the new fields:
-
- >>> request = TestRequest(my_new_login_field='luke', my_new_password_field='the_force')
-
- The plugin now extracts the credentials information from these new fields:
-
- >>> plugin.extractCredentials(request)
- {'login': 'luke', 'password': 'the_force'}
-
- Finally, we clear the session credentials using the logout method:
-
- >>> plugin.logout(TestRequest())
- True
- >>> print plugin.extractCredentials(TestRequest())
- None
-
- """
- implements(ICredentialsPlugin, IBrowserFormChallenger)
-
- loginpagename = 'loginForm.html'
- loginfield = 'login'
- passwordfield = 'password'
-
- def extractCredentials(self, request):
- """Extracts credentials from a session if they exist."""
- if not IHTTPRequest.providedBy(request):
- return None
- session = ISession(request)
- sessionData = session.get(
- 'zope.app.authentication.browserplugins')
- login = request.get(self.loginfield, None)
- password = request.get(self.passwordfield, None)
- credentials = None
-
- if login and password:
- credentials = SessionCredentials(login, password)
- elif not sessionData:
- return None
- sessionData = session[
- 'zope.app.authentication.browserplugins']
- if credentials:
- sessionData['credentials'] = credentials
- else:
- credentials = sessionData.get('credentials', None)
- if not credentials:
- return None
- return {'login': credentials.getLogin(),
- 'password': credentials.getPassword()}
-
- def challenge(self, request):
- """Challenges by redirecting to a login form.
-
- To illustrate, we'll create a test request:
-
- >>> from zope.publisher.browser import TestRequest
- >>> request = TestRequest()
-
- and confirm its response's initial status and 'location' header:
-
- >>> request.response.getStatus()
- 599
- >>> request.response.getHeader('location')
-
- When we issue a challenge using a session plugin:
-
- >>> plugin = SessionCredentialsPlugin()
- >>> plugin.challenge(request)
- True
-
- we get a redirect:
-
- >>> request.response.getStatus()
- 302
- >>> request.response.getHeader('location')
- 'http://127.0.0.1/@@loginForm.html?camefrom=%2F'
-
- The plugin redirects to the page defined by the loginpagename
- attribute:
-
- >>> plugin.loginpagename = 'mylogin.html'
- >>> plugin.challenge(request)
- True
- >>> request.response.getHeader('location')
- 'http://127.0.0.1/@@mylogin.html?camefrom=%2F'
-
- It also provides the request URL as a 'camefrom' GET style parameter.
- To illustrate, we'll pretend we've traversed a couple names:
-
- >>> env = {
- ... 'REQUEST_URI': '/foo/bar/folder/page%201.html?q=value',
- ... 'QUERY_STRING': 'q=value'
- ... }
- >>> request = TestRequest(environ=env)
- >>> request._traversed_names = [u'foo', u'bar']
- >>> request._traversal_stack = [u'page 1.html', u'folder']
- >>> request['REQUEST_URI']
- '/foo/bar/folder/page%201.html?q=value'
-
- When we challenge:
-
- >>> plugin.challenge(request)
- True
-
- We see the 'camefrom' points to the requested URL:
-
- >>> request.response.getHeader('location') # doctest: +ELLIPSIS
- '.../@@mylogin.html?camefrom=%2Ffoo%2Fbar%2Ffolder%2Fpage+1.html%3Fq%3Dvalue'
-
- This can be used by the login form to redirect the user back to the
- originating URL upon successful authentication.
- """
- if not IHTTPRequest.providedBy(request):
- return False
-
- site = hooks.getSite()
- # We need the traversal stack to complete the 'camefrom' parameter
- stack = request.getTraversalStack()
- stack.reverse()
- # Better to add the query string, if present
- query = request.get('QUERY_STRING')
-
- camefrom = '/'.join([request.getURL(path_only=True)] + stack)
- if query:
- camefrom = camefrom + '?' + query
- url = '%s/@@%s?%s' % (absoluteURL(site, request),
- self.loginpagename,
- urlencode({'camefrom': camefrom}))
- request.response.redirect(url)
- return True
-
- def logout(self, request):
- """Performs logout by clearing session data credentials."""
- if not IHTTPRequest.providedBy(request):
- return False
-
- sessionData = ISession(request)[
- 'zope.app.authentication.browserplugins']
- sessionData['credentials'] = None
- transaction.commit()
- return True
Deleted: zope.password/trunk/src/zope/password/session.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/session.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/session.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,28 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain="zope"
- >
-
- <utility
- name="Session Credentials"
- provides=".interfaces.ICredentialsPlugin"
- factory=".session.SessionCredentialsPlugin"
- />
-
- <class class=".session.SessionCredentialsPlugin">
-
- <implements
- interface="zope.annotation.interfaces.IAttributeAnnotatable"
- />
-
- <require
- permission="zope.ManageServices"
- interface=".session.IBrowserFormChallenger"
- set_schema=".session.IBrowserFormChallenger"
- />
-
- </class>
-
- <include package=".browser" file="session.zcml" />
-
-</configure>
Deleted: zope.password/trunk/src/zope/password/testing.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/testing.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/testing.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,26 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2007 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""zope.app.authentication common test related classes/functions/objects.
-
-$Id$
-"""
-
-__docformat__ = "reStructuredText"
-
-import os
-from zope.app.testing.functional import ZCMLLayer
-
-AppAuthenticationLayer = ZCMLLayer(
- os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
- __name__, 'AppAuthenticationLayer', allow_teardown=True)
Copied: zope.password/trunk/src/zope/password/testing.py (from rev 97555, zope.app.authentication/trunk/src/zope/app/authentication/placelesssetup.py)
===================================================================
--- zope.password/trunk/src/zope/password/testing.py (rev 0)
+++ zope.password/trunk/src/zope/password/testing.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Setup password managers as utilities
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.component import provideUtility
+from zope.password.interfaces import IPasswordManager
+from zope.password.password import PlainTextPasswordManager
+from zope.password.password import MD5PasswordManager
+from zope.password.password import SHA1PasswordManager
+from zope.password.password import SSHAPasswordManager
+
+
+def setUpPasswordManagers():
+ provideUtility(PlainTextPasswordManager(), IPasswordManager, 'Plain Text')
+ provideUtility(SSHAPasswordManager(), IPasswordManager, 'SSHA')
+ provideUtility(MD5PasswordManager(), IPasswordManager, 'MD5')
+ provideUtility(SHA1PasswordManager(), IPasswordManager, 'SHA1')
Modified: zope.password/trunk/src/zope/password/tests.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/tests.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/tests.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2004 Zope Corporation and Contributors.
+# Copyright (c) 2009 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -11,123 +11,15 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Pluggable Authentication Service Tests
+"""Password Managers Tests
$Id$
"""
-__docformat__ = "reStructuredText"
-
import unittest
-
from zope.testing import doctest
-from zope.interface import implements
-from zope.component import provideUtility, provideAdapter
-from zope.component.eventtesting import getEvents, clearEvents
-from zope.publisher.interfaces import IRequest
-from zope.app.testing import placelesssetup, ztapi
-from zope.app.testing.setup import placefulSetUp, placefulTearDown
-from zope.session.interfaces import \
- IClientId, IClientIdManager, ISession, ISessionDataContainer
-from zope.session.session import \
- ClientId, Session, PersistentSessionDataContainer
-from zope.session.http import CookieClientIdManager
-from zope.publisher import base
-from zope.app.authentication.session import SessionCredentialsPlugin
-
-
-class TestClientId(object):
- implements(IClientId)
- def __new__(cls, request):
- return 'dummyclientidfortesting'
-
-def siteSetUp(test):
- placefulSetUp(site=True)
-
-def siteTearDown(test):
- placefulTearDown()
-
-def sessionSetUp(session_data_container_class=PersistentSessionDataContainer):
- placelesssetup.setUp()
- ztapi.provideAdapter(IRequest, IClientId, TestClientId)
- ztapi.provideAdapter(IRequest, ISession, Session)
- ztapi.provideUtility(IClientIdManager, CookieClientIdManager())
- sdc = session_data_container_class()
- ztapi.provideUtility(ISessionDataContainer, sdc, '')
-
-def nonHTTPSessionTestCaseSetUp(sdc_class=PersistentSessionDataContainer):
- # I am getting an error with ClientId and not TestClientId
- placelesssetup.setUp()
- ztapi.provideAdapter(IRequest, IClientId, ClientId)
- ztapi.provideAdapter(IRequest, ISession, Session)
- ztapi.provideUtility(IClientIdManager, CookieClientIdManager())
- sdc = sdc_class()
- ztapi.provideUtility(ISessionDataContainer, sdc, '')
-
-
-class NonHTTPSessionTestCase(unittest.TestCase):
- # Small test suite to catch an error with non HTTP protocols, like FTP
- # and SessionCredentialsPlugin.
- def setUp(self):
- nonHTTPSessionTestCaseSetUp()
-
- def tearDown(self):
- placefulTearDown()
-
- def test_exeractCredentials(self):
- plugin = SessionCredentialsPlugin()
-
- self.assertEqual(plugin.extractCredentials(base.TestRequest('/')), None)
-
- def test_challenge(self):
- plugin = SessionCredentialsPlugin()
-
- self.assertEqual(plugin.challenge(base.TestRequest('/')), False)
-
- def test_logout(self):
- plugin = SessionCredentialsPlugin()
-
- self.assertEqual(plugin.logout(base.TestRequest('/')), False)
-
-
def test_suite():
return unittest.TestSuite((
- doctest.DocTestSuite('zope.app.authentication.interfaces'),
- doctest.DocTestSuite('zope.app.authentication.password'),
- doctest.DocTestSuite('zope.app.authentication.generic'),
- doctest.DocTestSuite('zope.app.authentication.httpplugins'),
- doctest.DocTestSuite('zope.app.authentication.ftpplugins'),
- doctest.DocTestSuite('zope.app.authentication.groupfolder'),
- doctest.DocFileSuite('principalfolder.txt',
- setUp=placelesssetup.setUp,
- tearDown=placelesssetup.tearDown),
- doctest.DocTestSuite('zope.app.authentication.principalfolder',
- setUp=placelesssetup.setUp,
- tearDown=placelesssetup.tearDown),
- doctest.DocTestSuite('zope.app.authentication.idpicker'),
- doctest.DocTestSuite('zope.app.authentication.session',
- setUp=siteSetUp,
- tearDown=siteTearDown),
- doctest.DocFileSuite('README.txt',
- setUp=siteSetUp,
- tearDown=siteTearDown,
- globs={'provideUtility': provideUtility,
- 'provideAdapter': provideAdapter,
- 'getEvents': getEvents,
- 'clearEvents': clearEvents,
- 'subscribe': ztapi.subscribe,
- }),
- doctest.DocFileSuite('groupfolder.txt',
- setUp=placelesssetup.setUp,
- tearDown=placelesssetup.tearDown,
- ),
- doctest.DocFileSuite('vocabulary.txt',
- setUp=placelesssetup.setUp,
- tearDown=placelesssetup.tearDown,
- ),
- unittest.makeSuite(NonHTTPSessionTestCase),
+ doctest.DocTestSuite('zope.password.password'),
))
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
Deleted: zope.password/trunk/src/zope/password/vocabulary.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/vocabulary.py 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/vocabulary.py 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,96 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Plugin Vocabulary.
-
-This vocabulary provides terms for authentication utility plugins.
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-
-import zope.dublincore.interfaces
-from zope import interface, component, i18n
-from zope.schema import vocabulary
-from zope.schema.interfaces import IVocabularyFactory
-
-from zope.app.authentication.i18n import ZopeMessageFactory as _
-
-from zope.app.authentication import interfaces
-
-UTILITY_TITLE = _(
- 'zope.app.authentication.vocabulary-utility-plugin-title',
- '${name} (a utility)')
-CONTAINED_TITLE = _(
- 'zope.app.authentication.vocabulary-contained-plugin-title',
- '${name} (in contents)')
-MISSING_TITLE = _(
- 'zope.app.authentication.vocabulary-missing-plugin-title',
- '${name} (not found; deselecting will remove)')
-
-def _pluginVocabulary(context, interface, attr_name):
- """Vocabulary that provides names of plugins of a specified interface.
-
- Given an interface, the options should include the unique names of all of
- the plugins that provide the specified interface for the current context--
- which is expected to be a pluggable authentication utility, hereafter
- referred to as a PAU).
-
- These plugins may be objects contained within the PAU ("contained
- plugins"), or may be utilities registered for the specified
- interface, found in the context of the PAU ("utility plugins").
- Contained plugins mask utility plugins of the same name.
-
- The vocabulary also includes the current values of the PAU even if they do
- not correspond to a contained or utility plugin.
- """
- terms = {}
- isPAU = interfaces.IPluggableAuthentication.providedBy(context)
- if isPAU:
- for k, v in context.items():
- if interface.providedBy(v):
- dc = zope.dublincore.interfaces.IDCDescriptiveProperties(
- v, None)
- if dc is not None and dc.title:
- title = dc.title
- else:
- title = k
- terms[k] = vocabulary.SimpleTerm(
- k, k.encode('base64').strip(), i18n.Message(
- CONTAINED_TITLE, mapping={'name': title}))
- utils = component.getUtilitiesFor(interface, context)
- for nm, util in utils:
- if nm not in terms:
- terms[nm] = vocabulary.SimpleTerm(
- nm, nm.encode('base64').strip(), i18n.Message(
- UTILITY_TITLE, mapping={'name': nm}))
- if isPAU:
- for nm in set(getattr(context, attr_name)):
- if nm not in terms:
- terms[nm] = vocabulary.SimpleTerm(
- nm, nm.encode('base64').strip(), i18n.Message(
- MISSING_TITLE, mapping={'name': nm}))
- return vocabulary.SimpleVocabulary(
- [term for nm, term in sorted(terms.items())])
-
-def authenticatorPlugins(context):
- return _pluginVocabulary(
- context, interfaces.IAuthenticatorPlugin, 'authenticatorPlugins')
-
-interface.alsoProvides(authenticatorPlugins, IVocabularyFactory)
-
-def credentialsPlugins(context):
- return _pluginVocabulary(
- context, interfaces.ICredentialsPlugin, 'credentialsPlugins')
-
-interface.alsoProvides(credentialsPlugins, IVocabularyFactory)
Deleted: zope.password/trunk/src/zope/password/vocabulary.txt
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/vocabulary.txt 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/vocabulary.txt 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1,203 +0,0 @@
-============
-Vocabularies
-============
-
-The vocabulary module provides vocabularies for the authenticator plugins and
-the credentials plugins.
-
-The options should include the unique names of all of the plugins that provide
-the appropriate interface (interfaces.ICredentialsPlugin or
-interfaces.IAuthentiatorPlugin, respectively) for the current context-- which
-is expected to be a pluggable authentication utility, hereafter referred to as
-a PAU.
-
-These names may be for objects contained within the PAU ("contained
-plugins"), or may be utilities registered for the specified interface,
-found in the context of the PAU ("utility plugins"). Contained
-plugins mask utility plugins of the same name. They also may be names
-currently selected in the PAU that do not actually have a
-corresponding plugin at this time.
-
-Here is a short example of how the vocabulary should work. Let's say we're
-working with authentication plugins. We'll create some faux
-authentication plugins, and register some of them as utilities and put
-others in a faux PAU.
-
- >>> from zope.app.authentication import interfaces
- >>> from zope import interface, component
- >>> class DemoPlugin(object):
- ... interface.implements(interfaces.IAuthenticatorPlugin)
- ... def __init__(self, name):
- ... self.name = name
- ...
- >>> utility_plugins = dict(
- ... (i, DemoPlugin(u'Plugin %d' % i)) for i in range(4))
- >>> contained_plugins = dict(
- ... (i, DemoPlugin(u'Plugin %d' % i)) for i in range(1, 5))
- >>> sorted(utility_plugins.keys())
- [0, 1, 2, 3]
- >>> for p in utility_plugins.values():
- ... component.provideUtility(p, name=p.name)
- ...
- >>> sorted(contained_plugins.keys()) # 1 will mask utility plugin 1
- [1, 2, 3, 4]
- >>> class DemoAuth(dict):
- ... interface.implements(interfaces.IPluggableAuthentication)
- ... def __init__(self, *args, **kwargs):
- ... super(DemoAuth, self).__init__(*args, **kwargs)
- ... self.authenticatorPlugins = (u'Plugin 3', u'Plugin X')
- ... self.credentialsPlugins = (u'Plugin 4', u'Plugin X')
- ...
- >>> auth = DemoAuth((p.name, p) for p in contained_plugins.values())
-
- >>> @component.adapter(interface.Interface)
- ... @interface.implementer(component.IComponentLookup)
- ... def getSiteManager(context):
- ... return component.getGlobalSiteManager()
- ...
- >>> component.provideAdapter(getSiteManager)
-
-We are now ready to create a vocabulary that we can use. The context is
-our faux authentication utility, `auth`.
-
- >>> from zope.app.authentication import vocabulary
- >>> vocab = vocabulary.authenticatorPlugins(auth)
-
-Iterating over the vocabulary results in all of the terms, in a relatively
-arbitrary order of their names. (This vocabulary should typically use a
-widget that sorts values on the basis of localized collation order of the
-term titles.)
-
- >>> [term.value for term in vocab] # doctest: +NORMALIZE_WHITESPACE
- [u'Plugin 0', u'Plugin 1', u'Plugin 2', u'Plugin 3', u'Plugin 4',
- u'Plugin X']
-
-Similarly, we can use `in` to test for the presence of values in the
-vocabulary.
-
- >>> ['Plugin %s' % i in vocab for i in range(-1, 6)]
- [False, True, True, True, True, True, False]
- >>> 'Plugin X' in vocab
- True
-
-The length reports the expected value.
-
- >>> len(vocab)
- 6
-
-One can get a term for a given value using `getTerm()`; its token, in
-turn, should also return the same effective term from `getTermByToken`.
-
- >>> values = ['Plugin 0', 'Plugin 1', 'Plugin 2', 'Plugin 3', 'Plugin 4',
- ... 'Plugin X']
- >>> for val in values:
- ... term = vocab.getTerm(val)
- ... assert term.value == val
- ... term2 = vocab.getTermByToken(term.token)
- ... assert term2.token == term.token
- ... assert term2.value == val
- ...
-
-The terms have titles, which are message ids that show the plugin title or id
-and whether the plugin is a utility or just contained in the auth utility.
-We'll give one of the plugins a dublin core title just to show the
-functionality.
-
- >>> import zope.dublincore.interfaces
- >>> class ISpecial(interface.Interface):
- ... pass
- ...
- >>> interface.directlyProvides(contained_plugins[1], ISpecial)
- >>> class DemoDCAdapter(object):
- ... interface.implements(
- ... zope.dublincore.interfaces.IDCDescriptiveProperties)
- ... component.adapts(ISpecial)
- ... def __init__(self, context):
- ... pass
- ... title = u'Special Title'
- ...
- >>> component.provideAdapter(DemoDCAdapter)
-
-We need to regenerate the vocabulary, since it calculates all of its data at
-once.
-
- >>> vocab = vocabulary.authenticatorPlugins(auth)
-
-Now we'll check the titles. We'll have to translate them to see what we
-expect.
-
- >>> from zope import i18n
- >>> import pprint
- >>> pprint.pprint([i18n.translate(term.title) for term in vocab])
- [u'Plugin 0 (a utility)',
- u'Special Title (in contents)',
- u'Plugin 2 (in contents)',
- u'Plugin 3 (in contents)',
- u'Plugin 4 (in contents)',
- u'Plugin X (not found; deselecting will remove)']
-
-credentialsPlugins
-------------------
-
-For completeness, we'll do the same review of the credentialsPlugins.
-
- >>> class DemoPlugin(object):
- ... interface.implements(interfaces.ICredentialsPlugin)
- ... def __init__(self, name):
- ... self.name = name
- ...
- >>> utility_plugins = dict(
- ... (i, DemoPlugin(u'Plugin %d' % i)) for i in range(4))
- >>> contained_plugins = dict(
- ... (i, DemoPlugin(u'Plugin %d' % i)) for i in range(1, 5))
- >>> for p in utility_plugins.values():
- ... component.provideUtility(p, name=p.name)
- ...
- >>> auth = DemoAuth((p.name, p) for p in contained_plugins.values())
- >>> vocab = vocabulary.credentialsPlugins(auth)
-
-Iterating over the vocabulary results in all of the terms, in a relatively
-arbitrary order of their names. (This vocabulary should typically use a
-widget that sorts values on the basis of localized collation order of the term
-titles.) Similarly, we can use `in` to test for the presence of values in the
-vocabulary. The length reports the expected value.
-
- >>> [term.value for term in vocab] # doctest: +NORMALIZE_WHITESPACE
- [u'Plugin 0', u'Plugin 1', u'Plugin 2', u'Plugin 3', u'Plugin 4',
- u'Plugin X']
- >>> ['Plugin %s' % i in vocab for i in range(-1, 6)]
- [False, True, True, True, True, True, False]
- >>> 'Plugin X' in vocab
- True
- >>> len(vocab)
- 6
-
-One can get a term for a given value using `getTerm()`; its token, in
-turn, should also return the same effective term from `getTermByToken`.
-
- >>> values = ['Plugin 0', 'Plugin 1', 'Plugin 2', 'Plugin 3', 'Plugin 4',
- ... 'Plugin X']
- >>> for val in values:
- ... term = vocab.getTerm(val)
- ... assert term.value == val
- ... term2 = vocab.getTermByToken(term.token)
- ... assert term2.token == term.token
- ... assert term2.value == val
- ...
-
-The terms have titles, which are message ids that show the plugin title or id
-and whether the plugin is a utility or just contained in the auth utility.
-We'll give one of the plugins a dublin core title just to show the
-functionality. We need to regenerate the vocabulary, since it calculates all
-of its data at once. Then we'll check the titles. We'll have to translate
-them to see what we expect.
-
- >>> interface.directlyProvides(contained_plugins[1], ISpecial)
- >>> vocab = vocabulary.credentialsPlugins(auth)
- >>> pprint.pprint([i18n.translate(term.title) for term in vocab])
- [u'Plugin 0 (a utility)',
- u'Special Title (in contents)',
- u'Plugin 2 (in contents)',
- u'Plugin 3 (in contents)',
- u'Plugin 4 (in contents)',
- u'Plugin X (not found; deselecting will remove)']
Deleted: zope.password/trunk/src/zope/password/zope.app.authentication-configure.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/zope.app.authentication-configure.zcml 2009-03-06 09:38:55 UTC (rev 97555)
+++ zope.password/trunk/src/zope/password/zope.app.authentication-configure.zcml 2009-03-06 11:47:04 UTC (rev 97560)
@@ -1 +0,0 @@
-<include package="zope.app.authentication" />
More information about the Checkins
mailing list