[Checkins] SVN: z3c.authenticator/ Import initial implementation
Roger Ineichen
roger at projekt01.ch
Sun Mar 23 07:03:23 EDT 2008
Log message for revision 84862:
Import initial implementation
Changed:
A z3c.authenticator/branches/
A z3c.authenticator/tags/
A z3c.authenticator/trunk/
A z3c.authenticator/trunk/CHANGES.txt
A z3c.authenticator/trunk/README.txt
A z3c.authenticator/trunk/bootstrap.py
A z3c.authenticator/trunk/buildout.cfg
A z3c.authenticator/trunk/externals/
A z3c.authenticator/trunk/setup.py
A z3c.authenticator/trunk/src/
A z3c.authenticator/trunk/src/z3c/
A z3c.authenticator/trunk/src/z3c/__init__.py
A z3c.authenticator/trunk/src/z3c/authenticator/
A z3c.authenticator/trunk/src/z3c/authenticator/README.txt
A z3c.authenticator/trunk/src/z3c/authenticator/__init__.py
A z3c.authenticator/trunk/src/z3c/authenticator/authentication.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/
A z3c.authenticator/trunk/src/z3c/authenticator/browser/__init__.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/browser/configure.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/browser/edit.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/group.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/group.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/browser/login.pt
A z3c.authenticator/trunk/src/z3c/authenticator/browser/login.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/login.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/browser/schemasearch.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/user.py
A z3c.authenticator/trunk/src/z3c/authenticator/browser/user.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/credential.py
A z3c.authenticator/trunk/src/z3c/authenticator/credential.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/event.py
A z3c.authenticator/trunk/src/z3c/authenticator/group.py
A z3c.authenticator/trunk/src/z3c/authenticator/group.txt
A z3c.authenticator/trunk/src/z3c/authenticator/group.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py
A z3c.authenticator/trunk/src/z3c/authenticator/principal.py
A z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/testing.py
A z3c.authenticator/trunk/src/z3c/authenticator/tests.py
A z3c.authenticator/trunk/src/z3c/authenticator/user.py
A z3c.authenticator/trunk/src/z3c/authenticator/user.zcml
A z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.py
A z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt
-=-
Added: z3c.authenticator/trunk/CHANGES.txt
===================================================================
--- z3c.authenticator/trunk/CHANGES.txt (rev 0)
+++ z3c.authenticator/trunk/CHANGES.txt 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,8 @@
+=======
+CHANGES
+=======
+
+Version 0.5.0 (unreleased)
+-------------------------
+
+- Initial Release
Property changes on: z3c.authenticator/trunk/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/README.txt
===================================================================
--- z3c.authenticator/trunk/README.txt (rev 0)
+++ z3c.authenticator/trunk/README.txt 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,6 @@
+This package provides an IAuthentication implementation for Zope3. Note that
+this implementation is independent of zope.app.authentication and it doesn't
+depend on that package. This means it doesn't even use the credential or
+authentication plugins offered from zope.app.authentication package.
+I only recommend using this package if you need to implement own authentication
+concepts and you don't like to use zope.app.authentication as dependency.
Property changes on: z3c.authenticator/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/bootstrap.py
===================================================================
--- z3c.authenticator/trunk/bootstrap.py (rev 0)
+++ z3c.authenticator/trunk/bootstrap.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 75940 2007-05-24 14:45:00Z srichter $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Property changes on: z3c.authenticator/trunk/bootstrap.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/buildout.cfg
===================================================================
--- z3c.authenticator/trunk/buildout.cfg (rev 0)
+++ z3c.authenticator/trunk/buildout.cfg 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,18 @@
+[buildout]
+develop = .
+ externals/z3c.contents
+ externals/z3c.table
+
+parts = test checker coverage
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.authenticator [test]
+
+[checker]
+recipe = lovely.recipe:importchecker
+path = src/z3c/authenticator
+
+[coverage]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
Added: z3c.authenticator/trunk/setup.py
===================================================================
--- z3c.authenticator/trunk/setup.py (rev 0)
+++ z3c.authenticator/trunk/setup.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation 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
+
+$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='z3c.authenticator',
+ version='0.5.0dev',
+ author = "Roger Ineichen and the Zope Community",
+ author_email = "zope3-dev at zope.org",
+ description = "IAuthentication implementation for for Zope3",
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ license = "ZPL 2.1",
+ keywords = "zope3 z3c json rpc tree",
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url = 'http://cheeseshop.python.org/pypi/z3c.authenticator',
+ packages = find_packages('src'),
+ include_package_data = True,
+ package_dir = {'':'src'},
+ namespace_packages = ['z3c'],
+ extras_require = dict(
+ test = [
+ 'z3c.template',
+ 'z3c.testing',
+ 'zope.app.publication',
+ 'zope.app.publisher',
+ 'zope.app.security',
+ 'zope.app.testing',
+ 'zope.configuration',
+ 'zope.pagetemplate',
+ 'zope.publisher',
+ 'zope.testbrowser',
+ ],
+ ),
+ install_requires = [
+ 'setuptools',
+ 'z3c.i18n',
+ 'z3c.template',
+ 'z3c.contents',
+ 'z3c.table',
+ 'z3c.form',
+ 'z3c.formui',
+ 'zope.app.component',
+ 'zope.app.container',
+ 'zope.component',
+ 'zope.interface',
+ 'zope.proxy',
+ 'zope.publisher',
+ 'zope.security',
+ 'zope.traversing',
+ 'zope.viewlet',
+
+ ],
+ zip_safe = False,
+)
Property changes on: z3c.authenticator/trunk/setup.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/__init__.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/__init__.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/__init__.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+ import pkg_resources
+ pkg_resources.declare_namespace(__name__)
+except ImportError:
+ import pkgutil
+ __path__ = pkgutil.extend_path(__path__, __name__)
Property changes on: z3c.authenticator/trunk/src/z3c/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/README.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/README.txt (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/README.txt 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,445 @@
+========================
+X Authentication Utility
+========================
+
+The X Authentication Utility provides a framework for authenticating
+principals and associating information with them. It uses plugins and
+subscribers to get its work done.
+
+For a simple authentication utility to be used, it should be registered as a
+utility providing the `zope.app.security.interfaces.IAuthentication` interface.
+
+Our target is to support a handy IAuthentication utility which offers a simple
+API for custom IUser implementations.
+
+
+Security
+--------
+
+The Authenticator supports unique id tokens for principals. This means
+principal that get deleted and again added with the same id, login etc. do
+not have the same id again. We support this by generate a user id token
+generated by the host id, timestamp, a random string and the login attribute.
+
+
+What's different then in PluggableAuthentication
+------------------------------------------------
+
+We use a different pattern for IAuthenticatorPlugins in this implementation
+then used in PluggableAuthentication located in zope.app.authentication.
+Because the pluggable authentication is not very handy when it comes to
+implement custom principal information. The IPrincipalInfo hook supporting
+not propagate the password of a IInternalPrincipal is droped in this
+implementation.
+
+In our implementation we offer a IFoundPrincipal and IAuthenticatedPrincipal
+which are implemented as adapters for a IUser. This adapters do not offer
+it's context which is the real IUser.
+
+The Authenticator doesn't use a prefix. The usage of a prefix is only
+implemented in the IUserContainer.
+
+We do not use a prefix in the IUserContainer because of the used unique user
+id tokens. This will make sure that the same principal id doesn't get used at
+a later time (Common criteria). There is a ``add`` method which creates
+this id for you based on the login. The __setitem__ should not get used
+directly for adding IUser instances anymore. We heavily restricted the
+usage of this method. See the inline doc tests in __setitem__ for more info.
+
+
+Authentication
+==============
+
+The primary job of Authenticator is to authenticate principals. It uses
+two types of plug-ins in its work:
+
+ - Credentials Plugins
+
+ - Authenticator Plugins
+
+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 Authenticator returns a principal object,
+if it can. The X Autentication utility 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
+Authenticator 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.
+
+
+Principal
+---------
+
+First we create a principal:
+
+ >>> from z3c.authenticator import interfaces
+ >>> from z3c.authenticator.user import User
+ >>> login = u'bob'
+ >>> password = u'secret'
+ >>> title = u'Bob'
+ >>> p = User(login, password, title)
+
+Such a principal provides the following attributes be default
+
+ >>> p.login
+ u'bob'
+
+ >>> p.password
+ u'secret'
+
+ >>> p.title
+ u'Bob'
+
+and IUser:
+
+ >>> interfaces.IUser.providedBy(p)
+ True
+
+
+Authenticator Plugin
+--------------------
+
+First setup a UserContainer which will store the principals:
+
+ >>> from z3c.authenticator.user import UserContainer
+ >>> authPlugin = UserContainer()
+
+Now we have a UserContainer that provides a IUserContainer:
+
+ >>> interfaces.IUserContainer.providedBy(authPlugin)
+ True
+
+Now we will add the created principal to the principal container using the
+containers method ``add``:
+
+ >>> uid, user = authPlugin.add(p)
+
+The method returns the user id and the user object. The id get generated
+by the host IP address, the time, a random string and the user login attr.
+This token should be unique and guranted that it never get generated twice.
+This allows us to add, delete and add the same user again without that such a
+user will inherit existing permissions. We can test this token by compare it
+only with the __name__ of the object in this test since the token will
+different every testrun.
+
+ >>> user.__name__ == uid
+ True
+
+The returned user still is our previous added IUser
+
+ >>> user is p
+ True
+
+ >>> len(user.__name__)
+ 32
+
+ >>> user.login
+ u'bob'
+
+ >>> user.password
+ u'secret'
+
+ >>> user.title
+ u'Bob'
+
+let's register the UserContainer as a named IAuthenticatorPlugin utility:
+
+ >>> import zope.component
+ >>> zope.component.provideUtility(authPlugin,
+ ... provides=interfaces.IAuthenticatorPlugin,
+ ... name='My Authenticator Plugin')
+
+
+Credentials Plugin
+------------------
+
+After setup the user and user container, we'll create a simple credentials
+plugin:
+
+ >>> import zope.interface
+ >>> import zope.component
+
+ >>> class MyCredentialsPlugin(object):
+ ...
+ ... zope.interface.implements(interfaces.ICredentialsPlugin)
+ ...
+ ... def extractCredentials(self, request):
+ ... return {'login':request.get('login', ''),
+ ... 'password':request.get('password', '')}
+ ...
+ ... 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 or
+it could be stored in the Authenticator attribute credentialsPlugins.
+Use the first and register the plugina utility:
+
+ >>> myCredentialsPlugin = MyCredentialsPlugin()
+ >>> zope.component.provideUtility(myCredentialsPlugin,
+ ... name='My Credentials Plugin')
+
+
+AuthenticatedPrincipal and FoundPrincipal
+-----------------------------------------
+
+While authenticator plugins provide user, they are not responsible for
+creating principals. This function is performed by the Authenticator:
+
+ >>> from z3c.authenticator.principal import AuthenticatedPrincipal
+ >>> from z3c.authenticator.principal import FoundPrincipal
+ >>> zope.component.provideAdapter(AuthenticatedPrincipal,
+ ... provides=interfaces.IAuthenticatedPrincipal)
+
+ >>> zope.component.provideAdapter(FoundPrincipal,
+ ... provides=interfaces.IFoundPrincipal)
+
+
+Configuring Authenticator
+-------------------------
+
+Finally, we'll create the Authenticator itself:
+
+ >>> from z3c.authenticator import authentication
+ >>> auth = authentication.Authenticator()
+
+and configure it with the two plugins:
+
+ >>> auth.credentialsPlugins = ('My Credentials Plugin', )
+ >>> auth.authenticatorPlugins = ('My Authenticator Plugin', )
+
+
+Authenticate
+------------
+
+We can now use the Authenticator to authenticate a sample request:
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> print auth.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:
+
+ >>> request = TestRequest(form={'login':'let me in!', 'password':'secret'})
+ >>> print auth.authenticate(request)
+ None
+
+However, if we provide the proper credentials:
+
+ >>> request = TestRequest(form={'login':'bob', 'password':'secret'})
+ >>> bob = auth.authenticate(request)
+ >>> bob
+ <AuthenticatedPrincipal...>
+
+ >>> interfaces.IAuthenticatedPrincipal.providedBy(bob)
+ True
+
+we get an authenticated principal.
+
+
+Change login
+------------
+
+Change the login of a principal is a allwas a critical task because such a
+login together with a password is the key to our implemenation. Let's try to
+change the login and check if everything is correct. We can do this by get the
+principal from the UserContainer and change the login on the IUser
+implementation:
+
+ >>> internal = authPlugin[bob.id]
+ >>> internal.login = u'bob2'
+
+Now we should be able to login with the new login:
+
+ >>> request = TestRequest(form={'login':'bob2', 'password':'secret'})
+ >>> bob2 = auth.authenticate(request)
+ >>> bob2
+ <AuthenticatedPrincipal ...>
+
+ >>> bob2.title
+ u'Bob'
+
+But not with the old one:
+
+ >>> request = TestRequest(form={'login':'bob', 'password':'secret'})
+ >>> auth.authenticate(request) == None
+ True
+
+The user bob has still the same id as bob2 since the user id token doesn't
+get changed be changing the login:
+
+ >>> bob.id == bob2.id
+ True
+
+
+Events
+------
+
+Authenticate principal will create events.
+
+ >>> from zope.component.eventtesting import getEvents
+ >>> from zope.component.eventtesting import clearEvents
+
+We can verify that the appropriate event was published:
+
+ >>> clearEvents()
+ >>> request = TestRequest(form={'login':'bob2', 'password':'secret'})
+ >>> bobAgain = auth.authenticate(request)
+
+And the principal attribute in the event provides the authenticated principal:
+
+ >>> [event] = getEvents(interfaces.IAuthenticatedPrincipalCreated)
+ >>> event.principal is bobAgain
+ True
+
+ >>> event.principal
+ <AuthenticatedPrincipal ...>
+
+ >>> event.request is request
+ True
+
+The principal has the id, title, and description.
+
+ >>> event.principal.title
+ u'Bob'
+
+ >>> event.principal.id == uid
+ True
+
+ >>> event.principal.description
+ u''
+
+We provide subscribers to these events that can be used for doing custom
+processing. Note, the principal attibute provides a IAuthenticatedPrincipal:
+
+ >>> def addInfo(event):
+ ... id = event.principal.id
+ ... event.principal.description = 'Description for: %s' % id
+
+ >>> zope.component.provideHandler(addInfo,
+ ... [interfaces.IAuthenticatedPrincipalCreated])
+
+Now, if we authenticate a principal, its description is set:
+
+ >>> principal = auth.authenticate(request)
+ >>> principal.description
+ u'Description for: ...'
+
+
+Customization
+-------------
+
+Let's show you how the existing pattern can get used in a real use case. In
+the next sample we like to provide a additional email attribute for principals.
+First we have to define the interfaces declaring the email attribute:
+
+ >>> class IMyEmail(zope.interface.Interface):
+ ... email = zope.schema.TextLine(
+ ... title=u'EMail',
+ ... description=u'The EMail')
+
+ >>> class IMyUser(IMyEmail, interfaces.IUser):
+ ... """Custom IUser interface."""
+
+ >>> class IMyFoundPrincipal(IMyEmail, interfaces.IFoundPrincipal):
+ ... """Custom IIMyFoundrincipal interface."""
+
+ >>> class IMyAuthenticatedPrincipal(IMyEmail,
+ ... interfaces.IAuthenticatedPrincipal):
+ ... """Custom IAuthenticatedPrincipal interface."""
+
+After the schema, we define a custom principal implementation implementing
+this interface:
+
+ >>> class MyUser(User):
+ ... zope.interface.implements(IMyUser)
+ ... def __init__(self, login, password, title, description, email):
+ ... super(MyUser, self).__init__(login, password, title,
+ ... description)
+ ... self.email = email
+
+Now we have to define the AuthenticatedPrincipal for MyUser:
+
+ >>> class MyAuthenticatedPrincipal(AuthenticatedPrincipal):
+ ... zope.interface.implements(IMyAuthenticatedPrincipal)
+ ... def __init__(self, principal):
+ ... super(MyAuthenticatedPrincipal, self).__init__(principal)
+ ... self.email = principal.email
+
+And we have to define the FoundPrincipal for MyUser:
+
+ >>> class MyFoundPrincipal(FoundPrincipal):
+ ... zope.interface.implements(IMyFoundPrincipal)
+ ... def __init__(self, principal):
+ ... super(MyFoundPrincipal, self).__init__(principal)
+ ... self.email = principal.email
+
+Note that you can provide different attributes for the found and authenticated
+principal if needed. That's up to you what you like to do with this attributes
+later.
+
+Now we need to register our custom authenticated and found principal as
+adapters:
+
+ >>> zope.component.provideAdapter(MyAuthenticatedPrincipal,
+ ... provides=interfaces.IAuthenticatedPrincipal)
+
+ >>> zope.component.provideAdapter(MyFoundPrincipal,
+ ... provides=interfaces.IFoundPrincipal)
+
+Now we can use them without any other event subscriber or other registration
+in our principal container. Let's add a principal tho this container:
+
+ >>> p = MyUser(u'max', u'password', u'Max', u'', u'max at foobar.com')
+ >>> token, max = authPlugin.add(p)
+ >>> len(token)
+ 32
+
+ >>> max.__name__ == token
+ True
+
+ >>> max.password
+ u'password'
+
+ >>> max.title
+ u'Max'
+
+ >>> max.email
+ u'max at foobar.com'
+
+Let's try to authenticate...
+
+ >>> request = TestRequest(form={'login':'max', 'password':'password'})
+ >>> authenticated = auth.authenticate(request)
+
+and check your authenticated principal:
+
+ >>> interfaces.IAuthenticatedPrincipal.providedBy(authenticated)
+ True
+
+ >>> authenticated
+ <MyAuthenticatedPrincipal ...>
+
+ >>> authenticated.id == token
+ True
+
+ >>> authenticated.email
+ u'max at foobar.com'
+
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/__init__.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/__init__.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/__init__.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1 @@
+# make a package
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/authentication.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/authentication.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/authentication.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,177 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.event
+from zope.schema.interfaces import ISourceQueriables
+from zope.location.interfaces import ILocation
+
+from zope.app.component import queryNextUtility
+from zope.app.container import btree
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import PrincipalLookupError
+
+from z3c.authenticator import interfaces
+from z3c.authenticator import event
+
+
+class Authenticator(btree.BTreeContainer):
+ """See z3c.authentication.interfaces.IAuthenticator."""
+
+ zope.interface.implements(IAuthentication,
+ interfaces.IAuthenticator, ISourceQueriables)
+
+ authenticatorPlugins = ()
+ credentialsPlugins = ()
+
+ def _plugins(self, names, interface):
+ for name in names:
+ plugin = self.get(name)
+ if not interface.providedBy(plugin):
+ plugin = zope.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)
+ if credentials is None:
+ # do not invoke the auth plugin without credentials
+ continue
+ for authplugin in authenticatorPlugins:
+ if authplugin is None:
+ continue
+ principal = authplugin.authenticateCredentials(credentials)
+ if principal is None:
+ continue
+
+ # create authenticated principal
+ authenticated = interfaces.IAuthenticatedPrincipal(principal)
+
+ # send the IAuthenticatedPrincipalCreated event
+ zope.event.notify(event.AuthenticatedPrincipalCreated(
+ self, authenticated, request))
+ return authenticated
+
+ return None
+
+ def getPrincipal(self, id):
+ for name, authplugin in self.getAuthenticatorPlugins():
+ principal = authplugin.queryPrincipal(id)
+ if principal is None:
+ continue
+
+ # create found principal
+ found = interfaces.IFoundPrincipal(principal)
+
+ # send the IFoundPrincipalCreated event
+ zope.event.notify(event.FoundPrincipalCreated(self, found))
+ return found
+
+ next = queryNextUtility(self, IAuthentication)
+ if next is not None:
+ return next.getPrincipal(id)
+ raise PrincipalLookupError(id)
+
+ def getQueriables(self):
+ for name, authplugin in self.getAuthenticatorPlugins():
+ queriable = zope.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)..
+ """
+ zope.component.adapts(
+ interfaces.IQuerySchemaSearch,
+ interfaces.IAuthenticator)
+
+ zope.interface.implements(
+ interfaces.IQueriableAuthenticator,
+ interfaces.IQuerySchemaSearch,
+ ILocation)
+
+ def __init__(self, authplugin, pau):
+ if ILocation.providedBy(authplugin):
+ 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 id
+
+
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/authentication.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/__init__.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/__init__.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/__init__.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,2 @@
+# make a package
+
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.event
+import zope.lifecycleevent
+from zope.traversing.browser import absoluteURL
+import zope.schema
+
+from z3c.i18n import MessageFactory as _
+from z3c.authenticator import interfaces
+from z3c.authenticator import group
+from z3c.authenticator import user
+from z3c.form import field
+from z3c.form import button
+from z3c.formui import form
+from z3c.pagelet import browser
+from z3c.configurator import configurator
+
+
+class IAddName(zope.interface.Interface):
+ """Object name."""
+
+ __name__ = zope.schema.TextLine(
+ title=u'Object Name',
+ description=u'Object Name',
+ required=True)
+
+
+# IUserContainer
+class AuthenticatorAddForm(form.AddForm):
+ """Authenticator add form."""
+
+ label = _('Add Authenticatorr.')
+ contentName = None
+
+ fields = field.Fields(IAddName)
+
+ def createAndAdd(self, data):
+ obj = user.UserContainer()
+ self.contentName = data.get('__name__', u'')
+ zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+ self.context[self.contentName] = obj
+
+ #configure
+ configurator.configure(obj, data)
+ return obj
+
+ def nextURL(self):
+ obj = self.context[self.contentName]
+ return '%s/index.html' % absoluteURL(obj, self.request)
+
+
+class AuthenticatorEditForm(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit Authenticator.')
+
+ fields = field.Fields(interfaces.IAuthenticator).select('credentialsPlugins',
+ 'authenticatorPlugins')
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,33 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/browser"
+ xmlns="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="z3c">
+
+ <z3c:pagelet
+ name="addAuthenticator.html"
+ for="..interfaces.IAuthenticator"
+ class=".authenticator.AuthenticatorAddForm"
+ permission="zope.ManageServices"
+ />
+
+ <z3c:pagelet
+ name="edit.html"
+ for="..interfaces.IAuthenticator"
+ class=".authenticator.AuthenticatorEditForm"
+ permission="zope.ManageServices"
+ />
+
+ <z3c:pagelet
+ name="contents.html"
+ for="..interfaces.IAuthenticator"
+ class="z3c.contents.browser.ContentsPage"
+ permission="zope.ManageSite"
+ />
+
+ <defaultView
+ for="..interfaces.IAuthenticator"
+ name="contents.html"
+ />
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/configure.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/configure.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/configure.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,20 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser"
+ i18n_domain="z3c">
+
+ <zope:adapter
+ for="..interfaces.IQuerySchemaSearch
+ zope.publisher.interfaces.browser.IBrowserRequest"
+ provides="zope.app.form.browser.interfaces.ISourceQueryView"
+ factory=".schemasearch.QuerySchemaSearchView"
+ />
+
+ <include file="authenticator.zcml" />
+ <include file="credential.zcml" />
+ <include file="group.zcml" />
+ <include file="login.zcml" />
+ <include file="user.zcml" />
+
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,41 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+from z3c.i18n import MessageFactory as _
+from z3c.form import field
+from z3c.formui import form
+
+from z3c.authenticator import interfaces
+
+
+class SessionCredentialsPluginEditForm(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit SessionCredentialsPlugin.')
+
+ fields = field.Fields(interfaces.ISessionCredentialsPlugin).select(
+ 'loginpagename', 'loginfield', 'passwordfield')
+
+
+class HTTPBasicAuthCredentialsPluginEditForm(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit HTTPBasicAuthCredentialsPlugin.')
+
+ fields = field.Fields(interfaces.IHTTPBasicAuthCredentialsPlugin).select(
+ 'realm')
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,31 @@
+<configure
+ xmlns="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="z3c">
+
+ <z3c:pagelet
+ name="edit.html"
+ for="..interfaces.ISessionCredentialsPlugin"
+ class=".credential.SessionCredentialsPluginEditForm"
+ permission="zope.ManageServices"
+ />
+
+ <z3c:pagelet
+ name="edit.html"
+ for="..interfaces.IHTTPBasicAuthCredentialsPlugin"
+ class=".credential.HTTPBasicAuthCredentialsPluginEditForm"
+ permission="zope.ManageServices"
+ />
+
+
+
+ <!-- register a loginForm.html page for your layer
+ <page
+ name="loginForm.html"
+ for="*"
+ template="loginform.pt"
+ permission="zope.Public"
+ />
+ -->
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/edit.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/edit.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/edit.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+
+from z3c.i18n import MessageFactory as _
+from z3c.authenticator import interfaces
+from z3c.formui import form
+from z3c.form import field
+
+from z3c.i18n import MessageFactory as _
+from z3c.authenticator import interfaces
+
+
+class AuthenticatorEditForm(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit Authenticator.')
+
+ fields = field.Fields(interfaces.IAuthenticator).select(
+ 'credentialsPlugins', 'authenticatorPlugins')
+
+
+class EditGroup(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit Group.')
+
+ form_fields = form.Fields(interfaces.IGroup).select('title',
+ 'description', 'principals')
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/edit.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/group.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/group.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/group.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,107 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.event
+import zope.lifecycleevent
+from zope.traversing.browser import absoluteURL
+import zope.schema
+
+from z3c.i18n import MessageFactory as _
+from z3c.authenticator import interfaces
+from z3c.authenticator import group
+from z3c.authenticator import user
+from z3c.form import field
+from z3c.form import button
+from z3c.formui import form
+from z3c.pagelet import browser
+from z3c.configurator import configurator
+
+
+class IAddName(zope.interface.Interface):
+ """Object name."""
+
+ __name__ = zope.schema.TextLine(
+ title=u'Object Name',
+ description=u'Object Name',
+ required=True)
+
+
+# IGroupContainer
+class GroupContainerAddForm(form.AddForm):
+ """GroupContainer add form."""
+
+ label = _('Add Group Container.')
+
+ fields = field.Fields(IAddName)
+ fields += field.Fields(interfaces.IGroupContainer).select(
+ 'prefix')
+
+ def createAndAdd(self, data):
+ obj = group.GroupContainer()
+ obj.prefix = data.get('prefix', u'')
+ self.contentName = data.get('__name__', u'')
+ zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+ self.context[self.contentName] = obj
+
+ #configure
+ configurator.configure(obj, data)
+ return obj
+
+ def nextURL(self):
+ obj = self.context[self.contentName]
+ return '%s/index.html' % absoluteURL(obj, self.request)
+
+
+# IGroup
+class GroupAddForm(form.AddForm):
+ """Group add form."""
+
+ label = _('Add Group.')
+
+ fields = field.Fields(IAddName)
+ fields += field.Fields(interfaces.IGroup).select('title', 'description')
+
+ def createAndAdd(self, data):
+ title = data.get('title', u'')
+ description = data.get('description', u'')
+ obj = group.Group(title, description)
+ name = data.get('__name__', u'')
+ prefix = self.context.prefix
+ if not name.startswith(prefix):
+ name = prefix + name
+ self.contentName = name
+ zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+ self.context[self.contentName] = obj
+
+ #configure
+ configurator.configure(obj, data)
+ return obj
+
+ def nextURL(self):
+ obj = self.context[self.contentName]
+ return '%s/index.html' % absoluteURL(obj, self.request)
+
+
+class GroupEditForm(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit Group.')
+
+ fields = field.Fields(interfaces.IGroup).select('title', 'description')
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/group.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/group.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/group.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/group.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,31 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/browser"
+ xmlns="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="z3c">
+
+ <!-- Group -->
+ <z3c:pagelet
+ name="addGroup.html"
+ for="..interfaces.IGroupContainer"
+ class=".group.GroupAddForm"
+ permission="zope.ManageServices"
+ />
+
+ <z3c:pagelet
+ name="edit.html"
+ for="..interfaces.IGroup"
+ class=".group.GroupEditForm"
+ menu="zmi_views" title="Edit"
+ permission="zope.ManageServices"
+ />
+
+ <!-- GroupContainer -->
+ <z3c:pagelet
+ name="addGroupContainer.html"
+ for="..interfaces.IAuthenticator"
+ class=".group.GroupContainerAddForm"
+ permission="zope.ManageServices"
+ />
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/group.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/login.pt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/login.pt (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/login.pt 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,19 @@
+<metal:block use-macro="macro:form">
+ <div metal:fill-slot="viewspace">
+ <metal:block use-macro="macro:form-header">
+ header
+ </metal:block>
+ <p tal:content="view/message" i18n:translate="">Please provide Login Information</p>
+ <p tal:condition="python:request.get('lostPassword')"
+ i18n:translate="">The password coverage process was started by mail, check
+ your mailbox for more information.</p>
+ <metal:block use-macro="macro:widget-rows">
+ widgets
+ </metal:block>
+ </div>
+</metal:block>
+<div class="linkRow">
+ <a href="loginForm.html.html"
+ tal:attributes="href string:${request/URL/-1}/sendPassword.html">Ich habe
+ mein Passwort vergessen.</a>
+</div>
\ No newline at end of file
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/login.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/login.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/login.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/login.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2007 Projekt01 GmbH.
+# All Rights Reserved.
+#
+##############################################################################
+"""
+$Id: login.py 357 2007-03-15 17:17:37Z roger.ineichen $
+"""
+__docformat__ = "reStructuredText"
+
+from urllib import urlencode
+from urllib import quote
+import zope.interface
+import zope.component
+import zope.schema
+from zope.interface import invariant
+from zope.interface import Invalid
+from zope.publisher.browser import BrowserPage
+from zope.traversing.browser import absoluteURL
+from zope.security.proxy import removeSecurityProxy
+from zope.app.component import hooks
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import ILogout
+
+from z3c.form.interfaces import HIDDEN_MODE
+from z3c.form.interfaces import IWidgets
+from z3c.form import field
+from z3c.form import button
+from z3c.formui import form
+import z3c.schema.email
+from z3c.template.template import getPageTemplate
+from z3c.template.template import getLayoutTemplate
+
+from z3c.i18n import MessageFactory as _
+from z3c.authenticator import interfaces
+
+
+class LoginForm(form.Form):
+ """Login form."""
+
+ template = getPageTemplate()
+ layout = getLayoutTemplate()
+
+ fields = field.Fields(interfaces.ILoginSchema)
+ nextURL = None
+ prefix = ''
+
+ def getCameFrom(self):
+ camefrom = self.request.get('camefrom', None)
+ if camefrom is None:
+ site = hooks.getSite()
+ camefrom = '%s/index.html' % absoluteURL(site, self.request)
+ return camefrom
+
+ def updateWidgets(self):
+ self.widgets = zope.component.getMultiAdapter(
+ (self, self.request, self.getContent()), IWidgets)
+ self.widgets.prefix = ''
+ self.widgets.ignoreContext = True
+ self.widgets.ignoreReadonly = True
+ self.widgets.update()
+ self.widgets['camefrom'].mode = HIDDEN_MODE
+ self.widgets['camefrom'].value = self.getCameFrom()
+
+ @property
+ def message(self):
+ if IUnauthenticatedPrincipal.providedBy(self.request.principal):
+ return _(u'Please provide Login Information')
+ return u''
+
+ @button.buttonAndHandler(_('Log-in'))
+ def handleLogin(self, action):
+ """Handle the subscribe action will register and login a user."""
+ if not IUnauthenticatedPrincipal.providedBy(self.request.principal):
+ data, errors = self.widgets.extract()
+ self.nextURL = data['camefrom']
+
+ def __call__(self):
+ self.update()
+ if self.nextURL is not None:
+ self.request.response.redirect(self.nextURL)
+ return ""
+ else:
+ return self.layout()
+
+
+class SiteLogout(BrowserPage):
+
+ def __call__(self):
+ """Force logout and avoid to hang around the login form."""
+ auth = zope.component.getUtility(IAuthentication)
+ ILogout(auth).logout(self.request)
+ siteURL = absoluteURL(hooks.getSite(), self.request)
+ self.request.response.redirect(siteURL + '/loginForm.html')
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/login.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/login.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/login.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/login.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,28 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="xauth">
+
+ <!-- login form -->
+ <z3c:pagelet
+ name="loginForm.html"
+ for="*"
+ class=".login.LoginForm"
+ permission="zope.Public"
+ />
+
+ <z3c:template
+ template="login.pt"
+ for=".login.LoginForm"
+ />
+
+ <!-- logout form -->
+ <page
+ name="logout.html"
+ for="*"
+ class=".login.SiteLogout"
+ permission="zope.Public"
+ />
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/login.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/schemasearch.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/schemasearch.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/schemasearch.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,111 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+from zope.interface import implements
+from zope.i18n import translate
+from zope.schema import getFieldsInOrder
+from zope.app.zapi import getName, getPath
+from zope.app.form.utility import setUpWidgets, getWidgetsData
+from zope.app.form.interfaces import IInputWidget
+from zope.app.form.browser.interfaces import ISourceQueryView
+
+from z3c.i18n import MessageFactory as _
+
+
+search_label = _('search-button', 'Search')
+source_label = _(u"Source path")
+source_title = _(u"Path to the source utility")
+
+
+class QuerySchemaSearchView(object):
+ implements(ISourceQueryView)
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def render(self, name):
+ schema = self.context.schema
+ sourcename = getName(self.context)
+ sourcepath = getPath(self.context)
+ setUpWidgets(self, schema, IInputWidget, prefix=name+'.field')
+ html = []
+
+ # add sub title for source search field
+ html.append('<h4>%s</h4>' % sourcename)
+
+ # start row for path display field
+ html.append('<div class="row">')
+
+ # for each source add path of source
+ html.append(' <div class="label">')
+ label = translate(source_label, context=self.request)
+ title = translate(source_title, context=self.request)
+ html.append(' <label for="%s" title="%s">' % (sourcename, title))
+ html.append(' %s' % label)
+ html.append(' </label>')
+ html.append(' </div>')
+ html.append(' <div class="field">')
+ html.append(' %s' % sourcepath)
+ html.append(' </div>')
+ html.append('</div>')
+
+ # start row for search fields
+ html.append('<div class="row">')
+
+ for field_name, field in getFieldsInOrder(schema):
+ widget = getattr(self, field_name+'_widget')
+
+ # for each field add label...
+ html.append(' <div class="label">')
+ html.append(' <label for="%s" title="%s">'
+ % (widget.name, widget.hint))
+ html.append(' %s' % widget.label)
+ html.append(' </label>')
+ html.append(' </div>')
+
+ # ...and field widget
+ html.append(' <div class="field">')
+ html.append(' %s' % widget())
+
+ if widget.error():
+ html.append(' <div class="error">')
+ html.append(' %s' % widget.error())
+ html.append(' </div>')
+ html.append(' </div>')
+ # end row
+ html.append('</div>')
+
+ # add search button for search fields
+ html.append('<div class="row">')
+ html.append(' <div class="field">')
+ html.append(' <input type="submit" name="%s" value="%s" />'
+ % (name+'.search',
+ translate(search_label, context=self.request)))
+ html.append(' </div>')
+ html.append('</div>')
+
+ return '\n'.join(html)
+
+ def results(self, name):
+ if not (name+'.search' in self.request):
+ return None
+ schema = self.context.schema
+ setUpWidgets(self, schema, IInputWidget, prefix=name+'.field')
+ data = getWidgetsData(self, schema)
+ return self.context.search(data)
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/schemasearch.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/user.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/user.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/user.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,107 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.event
+import zope.lifecycleevent
+from zope.traversing.browser import absoluteURL
+import zope.schema
+
+from z3c.i18n import MessageFactory as _
+from z3c.authenticator import interfaces
+from z3c.authenticator import group
+from z3c.authenticator import user
+from z3c.form import field
+from z3c.form import button
+from z3c.formui import form
+from z3c.pagelet import browser
+from z3c.configurator import configurator
+
+
+class IAddName(zope.interface.Interface):
+ """Object name."""
+
+ __name__ = zope.schema.TextLine(
+ title=u'Object Name',
+ description=u'Object Name',
+ required=True)
+
+
+# IUserContainer
+class UserContainerAddForm(form.AddForm):
+ """UserContainer add form."""
+
+ label = _('Add User Container.')
+ contentName = None
+
+ fields = field.Fields(IAddName)
+
+ def createAndAdd(self, data):
+ obj = user.UserContainer()
+ self.contentName = data.get('__name__', u'')
+ zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+ self.context[self.contentName] = obj
+
+ #configure
+ configurator.configure(obj, data)
+ return obj
+
+ def nextURL(self):
+ obj = self.context[self.contentName]
+ return '%s/index.html' % absoluteURL(obj, self.request)
+
+
+# IUser
+class UserAddForm(form.AddForm):
+ """User add form."""
+
+ label = _('Add User.')
+
+ fields = field.Fields(IAddName)
+ fields += field.Fields(interfaces.IUser).select('login', 'password',
+ 'title', 'description', 'passwordManagerName')
+
+ def createAndAdd(self, data):
+ login = data.get('login', u'')
+ password = data.get('password', u'')
+ title = data.get('title', u'')
+ description = data.get('description', u'')
+ passwordManagerName = data.get('passwordManagerName', u'')
+ obj = user.User(login, password, title, description,
+ passwordManagerName)
+ self.contentName = data.get('__name__', u'')
+ zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+ self.context[self.contentName] = obj
+
+ #configure
+ configurator.configure(obj, data)
+ return obj
+
+ def nextURL(self):
+ obj = self.context[self.contentName]
+ return '%s/index.html' % absoluteURL(obj, self.request)
+
+
+class UserEditForm(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit User.')
+
+ fields = field.Fields(interfaces.IUser).select('login', 'password',
+ 'title', 'description', 'passwordManagerName')
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/user.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/browser/user.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/user.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/user.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,30 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/browser"
+ xmlns="http://namespaces.zope.org/browser"
+ xmlns:z3c="http://namespaces.zope.org/z3c"
+ i18n_domain="z3c">
+
+ <!-- UserContainer -->
+ <z3c:pagelet
+ name="addUserContainer.html"
+ for="..interfaces.IAuthenticator"
+ class=".user.UserContainerAddForm"
+ permission="zope.ManageServices"
+ />
+
+ <!-- User -->
+ <z3c:pagelet
+ name="addUser.html"
+ for="..interfaces.IUserContainer"
+ class=".user.UserAddForm"
+ permission="zope.ManageServices"
+ />
+
+ <z3c:pagelet
+ name="edit.html"
+ for="..interfaces.IUser"
+ class=".user.UserEditForm"
+ permission="zope.ManageServices"
+ />
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/browser/user.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,44 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="z3c">
+
+ <class class=".authentication.Authenticator">
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ permission="zope.ManageSite"
+ interface=".interfaces.IAuthenticator"
+ set_schema=".interfaces.IAuthenticator"
+ />
+ <require
+ permission="zope.ManageServices"
+ attributes="registrationManager"
+ />
+ </class>
+
+ <adapter
+ for=".interfaces.IQuerySchemaSearch
+ .interfaces.IAuthenticator"
+ provides=".interfaces.IQueriableAuthenticator"
+ factory=".authentication.QuerySchemaSearchAdapter"
+ />
+
+ <utility
+ component=".vocabulary.authenticatorPlugins"
+ name="Z3CAuthenticatorPlugins"
+ />
+
+ <utility
+ component=".vocabulary.credentialsPlugins"
+ name="Z3CCredentialsPlugins"
+ />
+
+ <include file="principal.zcml" />
+ <include file="group.zcml" />
+ <include file="user.zcml" />
+ <include file="credential.zcml" />
+
+ <include package=".browser" />
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/credential.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/credential.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/credential.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,367 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import base64
+import transaction
+import persistent
+from urllib import urlencode
+
+import zope.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.app.container import contained
+
+from z3c.authenticator import interfaces
+
+
+class HTTPBasicAuthCredentialsPlugin(persistent.Persistent,
+ contained.Contained):
+
+
+ zope.interface.implements(interfaces.IHTTPBasicAuthCredentialsPlugin)
+
+ realm = 'Zope Application Management'
+
+ 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
+
+
+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'
+
+ """
+ zope.interface.implements(interfaces.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 SessionCredentialsPlugin(persistent.Persistent, contained.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 zope.app.authentication.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 zope.publisher.tests.httprequest 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
+
+ """
+ zope.interface.implements(interfaces.ISessionCredentialsPlugin)
+
+ challengeProtocol = None
+
+ 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, None)
+ sessionData = session.get('z3c.authenticator.credential.session')
+ 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['z3c.authenticator.credential.session']
+ 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)['z3c.authenticator.credential.session']
+ sessionData['credentials'] = None
+ transaction.commit()
+ return True
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/credential.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/credential.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/credential.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/credential.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,27 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="zope">
+
+ <class class=".credential.HTTPBasicAuthCredentialsPlugin">
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IHTTPBasicAuthCredentialsPlugin"
+ set_schema=".interfaces.IHTTPBasicAuthCredentialsPlugin"
+ />
+ </class>
+
+ <class class=".credential.SessionCredentialsPlugin">
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IBrowserFormChallenger"
+ set_schema=".interfaces.IBrowserFormChallenger"
+ />
+ </class>
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/credential.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/event.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/event.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/event.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,92 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+
+from z3c.authenticator import interfaces
+
+
+# principal events
+class AuthenticatedPrincipalCreated(object):
+ """
+ >>> from zope.interface.verify import verifyObject
+ >>> event = AuthenticatedPrincipalCreated('authentication', 'principal',
+ ... 'request')
+ >>> verifyObject(interfaces.IAuthenticatedPrincipalCreated, event)
+ True
+ """
+
+ zope.interface.implements(interfaces.IAuthenticatedPrincipalCreated)
+
+ def __init__(self, authentication, principal, request):
+ self.authentication = authentication
+ self.principal = principal
+ self.request = request
+
+
+class FoundPrincipalCreated(object):
+ """
+ >>> from zope.interface.verify import verifyObject
+ >>> event = FoundPrincipalCreated('authentication', 'principal')
+ >>> verifyObject(interfaces.IFoundPrincipalCreated, event)
+ True
+ """
+
+ zope.interface.implements(interfaces.IFoundPrincipalCreated)
+
+ def __init__(self, authentication, principal):
+ self.authentication = authentication
+ self.principal = principal
+
+
+# group events
+class GroupAdded(object):
+ """Group added event
+
+ >>> from zope.interface.verify import verifyObject
+ >>> event = GroupAdded(u'group')
+ >>> verifyObject(IGroupAdded, event)
+ True
+ """
+
+ zope.interface.implements(interfaces.IGroupAdded)
+
+ def __init__(self, group):
+ self.group = group
+
+ def __repr__(self):
+ return "<GroupAdded %r>" % self.group.__name__
+
+
+class AbstractUsersChanged(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(AbstractUsersChanged):
+ zope.interface.implements(interfaces.IPrincipalsAddedToGroup)
+
+
+class PrincipalsRemovedFromGroup(AbstractUsersChanged):
+ zope.interface.implements(interfaces.IPrincipalsRemovedFromGroup)
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/event.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/group.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/group.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/group.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,313 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import BTrees.OOBTree
+import persistent
+
+import zope.interface
+import zope.component
+import zope.event
+from zope.interface import alsoProvides
+from zope.security.interfaces import IGroup
+from zope.security.interfaces import IGroupAwarePrincipal
+from zope.security.interfaces import IMemberAwareGroup
+
+from zope.app.container import btree
+from zope.app.container import contained
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import IAuthenticatedGroup
+from zope.app.security.interfaces import IEveryoneGroup
+from zope.app import zapi
+
+from z3c.authenticator import interfaces
+from z3c.authenticator import event
+
+
+class Group(persistent.Persistent, contained.Contained):
+ """An implementation of IGroup used by the group container."""
+
+ zope.interface.implements(interfaces.IGroup)
+
+ _principals = ()
+
+ def __init__(self, title=u'', description=u''):
+ 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)
+
+ oldset = set(old)
+ new = set(prinlist)
+ gid = self.__name__
+ removed = oldset - new
+ added = new - oldset
+ try:
+ parent._removePrincipalsFromGroup(removed, gid)
+ except AttributeError:
+ removed = None
+
+ try:
+ parent._addPrincipalsToGroup(added, gid)
+ except AttributeError:
+ added = None
+
+ if check:
+ try:
+ nocycles(new, [], zapi.principals().getPrincipal)
+ except GroupCycle:
+ # abort
+ self.setPrincipals(old, False)
+ raise
+ # now that we've gotten past the checks, fire the events.
+ if removed:
+ zope.event.notify(
+ event.PrincipalsRemovedFromGroup(removed, gid))
+ if added:
+ zope.event.notify(
+ event.PrincipalsAddedToGroup(added, gid))
+
+ principals = property(lambda self: self._principals, setPrincipals)
+
+ def __repr__(self):
+ return "<%s %s>" %(self.__class__.__name__, self.__name__)
+
+
+class GroupContainer(btree.BTreeContainer):
+
+ zope.interface.implements(interfaces.IGroupContainer,
+ interfaces.IQuerySchemaSearch,)
+
+ schema = interfaces.IGroupSearchCriteria
+
+ def __init__(self, prefix=u''):
+ self.prefix = prefix
+ super(GroupContainer,self).__init__()
+ # __inversemapping is used to map principals to groups
+ self.__inverseMapping = BTrees.OOBTree.OOBTree()
+
+ def __setitem__(self, name, group):
+ """Add a IGroup object within a correct id.
+
+ Create a GroupContainer
+
+ >>> gc = GroupContainer('groups')
+
+ Try to add something not providing IGroup
+ >>> try:
+ ... gc.__setitem__(u'groups.members', object())
+ ... except Exception, e:
+ ... print e
+ Group does not support IGroup!
+
+ Create a group and add them with a wrong prefix:
+
+ >>> group = Group(u'users')
+ >>> try:
+ ... gc.__setitem__(u'123', group)
+ ... except Exception, e:
+ ... print e
+ 'Wrong prefix used in group id!'
+
+ Add a login attr since __setitem__ is in need of one
+
+ >>> gc.__setitem__(u'groups.users', group)
+ """
+ # check if we store correct groups
+ if not interfaces.IGroup.providedBy(group):
+ raise TypeError('Group does not support IGroup!')
+
+ # check if the given id provides the used prefix
+ if not name.startswith(self.prefix):
+ raise KeyError('Wrong prefix used in group id!')
+
+ super(GroupContainer, self).__setitem__(name, group)
+ gid = group.__name__
+ self._addPrincipalsToGroup(group.principals, gid)
+ if group.principals:
+ zope.event.notify(
+ event.PrincipalsAddedToGroup(group.principals, gid))
+ zope.event.notify(event.GroupAdded(group))
+
+ def addGroup(self, id, group):
+ id = self.prefix + id
+ self[id] = group
+ return id, self[id]
+
+ def __delitem__(self, gid):
+ group = self[gid]
+ self._removePrincipalsFromGroup(group.principals, gid)
+ if group.principals:
+ zope.event.notify(
+ event.PrincipalsRemovedFromGroup(group.principals, gid))
+ super(GroupContainer, self).__delitem__(gid)
+
+ def _addPrincipalsToGroup(self, pids, gid):
+ for pid in pids:
+ self.__inverseMapping[pid] = (
+ self.__inverseMapping.get(pid, ()) + (gid,))
+
+ def _removePrincipalsFromGroup(self, pids, gid):
+ for pid in pids:
+ groups = self.__inverseMapping.get(pid)
+ if groups is None:
+ return
+ new = tuple([id for id in groups if id != gid])
+ if new:
+ self.__inverseMapping[pid] = new
+ else:
+ del self.__inverseMapping[pid]
+
+ def getGroupsForPrincipal(self, pid):
+ """Get groups the given principal belongs to"""
+ return self.__inverseMapping.get(pid, ())
+
+ def getPrincipalsForGroup(self, gid):
+ """Get principals which belong to the group"""
+ return self[gid].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 id
+
+ def authenticateCredentials(self, credentials):
+ # group container don't authenticate
+ pass
+
+ def queryPrincipal(self, id, default=None):
+ return self.get(id, default)
+
+
+class GroupPrincipal(object):
+
+ zope.interface.implements(interfaces.IGroupPrincipal)
+ zope.component.adapts(interfaces.IGroup)
+
+ def __init__(self, group):
+ self.id = group.__name__
+ self._group = group
+ self.groups = []
+
+ @property
+ def allGroups(self):
+ if self.groups:
+ seen = set()
+ auth = zope.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 = auth.getPrincipal(group_id)
+ stack.append(iter(group.groups))
+
+ @property
+ def title(self):
+ return self._group.title
+
+ @property
+ def description(self):
+ return self._group.description
+
+ @apply
+ def members():
+ def get(self):
+ return self._group.principals
+ def set(self, value):
+ self._group.principals = value
+ return property(get, set)
+
+ def getUsers(self):
+ return self._group.principals
+
+ def setUsers(self, value):
+ self._group.principals = value
+
+ def __repr__(self):
+ return "<%s %s>" %(self.__class__.__name__, self.id)
+
+
+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(pids, seen, getPrincipal):
+ for pid in pids:
+ if pid in seen:
+ raise GroupCycle(pid, seen)
+ seen.append(pid)
+ principal = getPrincipal(pid)
+ nocycles(principal.groups, seen, getPrincipal)
+ seen.pop()
+
+
+def specialGroups(event):
+ principal = event.principal
+ if (IGroup.providedBy(principal) or
+ not IGroupAwarePrincipal.providedBy(principal)):
+ return
+
+ everyone = zope.component.queryUtility(IEveryoneGroup)
+ if everyone is not None:
+ principal.groups.append(everyone.id)
+
+ auth = zope.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 interfaces.IGroupContainer.providedBy(plugin):
+ continue
+ principal.groups.extend(
+ [id for id in plugin.getGroupsForPrincipal(principal.id)])
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/group.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/group.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/group.txt (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/group.txt 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,581 @@
+=====
+Group
+=====
+
+Group container provide support for groups information stored in the ZODB.
+They are persistent, and must be contained within the IAuthentication that
+use them.
+
+
+Group
+-----
+
+Like other users, groups are created when they are needed.
+
+ >>> from z3c.authenticator import interfaces
+ >>> from z3c.authenticator.group import Group
+ >>> group1 = Group(u'groups')
+ >>> interfaces.IGroup.providedBy(group1)
+ True
+
+ >>> group1.title
+ u'groups'
+
+ >>> group1.description
+ u''
+
+ >>> group1.principals
+ ()
+
+
+GroupContainer
+--------------
+
+Group containers contain IGroup objects. A IAuthentication will adapt
+IFoundGroup to this IGroup objects.
+
+ >>> from z3c.authenticator.group import GroupContainer
+ >>> groups = GroupContainer('groups.')
+
+ >>> interfaces.IGroupContainer.providedBy(groups)
+ True
+
+We can add your previous created group to the group container using the
+addGroup method which returns the group id and group:
+
+ >>> gid, g1 = groups.addGroup(u'g1', group1)
+ >>> gid
+ u'groups.g1'
+
+ >>> interfaces.IGroup.providedBy(g1)
+ True
+
+ >>> g1.__name__
+ u'groups.g1'
+
+Note that when group is added, a GroupAdded event is generated:
+
+ >>> from zope.component.eventtesting import getEvents
+ >>> getEvents(interfaces.IGroupAdded)
+ [<GroupAdded u'groups.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 will setup a Authenticator utility:
+
+ >>> from z3c.authenticator.authentication import Authenticator
+ >>> sau = Authenticator()
+
+Give them a location and register them as a IAuthentication utility :
+
+ >>> import zope.component
+ >>> from zope.app.security.interfaces import IAuthentication
+ >>> rootFolder['sau'] = sau
+ >>> zope.component.provideUtility(sau, IAuthentication)
+
+We will create and register a new principals utility:
+
+ >>> zope.component.provideUtility(sau, IAuthentication)
+
+We also need to register the group athentication plugin:
+
+ >>> zope.component.provideUtility(groups,
+ ... provides=interfaces.IAuthenticatorPlugin,
+ ... name='My Group Plugin')
+
+After setup the group and group container, we will create a simple credentials
+plugin and add them to the authentication utility:
+
+ >>> import zope.interface
+ >>> from z3c.authenticator import interfaces
+
+ >>> class MyCredentialsPlugin(object):
+ ...
+ ... zope.interface.implements(interfaces.ICredentialsPlugin)
+ ...
+ ... def extractCredentials(self, request):
+ ... return {'login':request.get('login', ''),
+ ... 'password':request.get('password', '')}
+ ...
+ ... 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
+
+and configure and add the credential plugin to the Authenticator:
+
+ >>> myCredentialsPlugin = MyCredentialsPlugin()
+ >>> sau['credentials'] = myCredentialsPlugin
+ >>> sau.credentialsPlugins = ('credentials', )
+
+We also need a principal and a IAuthenticationPlugin:
+
+ >>> from z3c.authenticator.user import User
+ >>> p1 = User(u'p1', u'password', u'Principal 1')
+ >>> p2 = User(u'p2', u'password', u'Principal 2')
+ >>> p3 = User(u'p3', u'password', u'Principal 3')
+ >>> p4 = User(u'p4', u'password', u'Principal 4')
+
+ >>> from z3c.authenticator.user import UserContainer
+ >>> users = UserContainer()
+ >>> token1, p1 = users.add(p1)
+ >>> token2, p2 = users.add(p2)
+ >>> token3, p3 = users.add(p3)
+ >>> token4, p4 = users.add(p4)
+
+Add the GroupContainer and UserContainer to the Authenticator and
+set the correct plugin names
+
+ >>> sau['users'] = users
+ >>> sau['groups'] = groups
+ >>> sau.authenticatorPlugins = ('users', 'groups')
+
+
+Adding users to groups
+----------------------
+
+Now we can set the users on the group but first we need to register the
+IFoundPrincipal adapter for groups. The GroupPrincipal adapter provides this
+interface:
+
+ >>> from z3c.authenticator.group import GroupPrincipal
+ >>> zope.component.provideAdapter(GroupPrincipal,
+ ... provides=interfaces.IFoundPrincipal)
+
+And we also need to provide the IFoundPrincipal and IAuthenticatedPrincipal
+adapter for IPrincipal objects:
+
+ >>> from z3c.authenticator.principal import AuthenticatedPrincipal
+ >>> from z3c.authenticator.principal import FoundPrincipal
+ >>> zope.component.provideAdapter(AuthenticatedPrincipal,
+ ... provides=interfaces.IAuthenticatedPrincipal)
+
+ >>> zope.component.provideAdapter(FoundPrincipal,
+ ... provides=interfaces.IFoundPrincipal)
+
+And we need the ``setGroupsForPrincipal`` subscriber:
+
+ >>> from z3c.authenticator.group import setGroupsForPrincipal
+ >>> zope.component.provideHandler(setGroupsForPrincipal,
+ ... [interfaces.IPrincipalCreated])
+
+ >>> g1.principals = [p1.__name__, p2.__name__]
+ >>> g1.principals
+ (..., ...)
+
+ >>> g1.principals[0] == p1.__name__
+ True
+
+ >>> g1.principals[1] == p2.__name__
+ True
+
+Adding users fires an event.
+
+ >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
+ <PrincipalsAddedToGroup [..., ...] u'groups.g1'>
+
+We can now look up groups for the users:
+
+ >>> groups.getGroupsForPrincipal(p1.__name__)
+ (u'groups.g1',)
+
+Note that the group id is a concatenation of the group-folder prefix
+and the name of the group object within the folder.
+
+If we delete a group:
+
+ >>> del groups['groups.g1']
+
+then the groups folder loses the group information for that group's users:
+
+ >>> groups.getGroupsForPrincipal('p1')
+ ()
+
+but the principal information on the group is unchanged:
+
+ >>> g1.principals
+ (..., ...)
+
+ >>> g1.principals[0] == p1.__name__
+ True
+
+ >>> g1.principals[1] == p2.__name__
+ True
+
+It also fires an event showing that the users are removed from the groups.
+
+ >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
+ <PrincipalsRemovedFromGroup [..., ...] 'groups.g1'>
+
+Adding the group again within a different name will make the groups
+available for the principal. Let's use a different group name:
+
+ >>> groups['groups.G1'] = g1
+
+ >>> groups.getGroupsForPrincipal(p1.__name__)
+ (u'groups.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 [..., ...] u'groups.G1'>
+
+In terms of member events (members added and removed from groups), we have
+now seen that events are fired when a group object is added and when it is
+removed from a group container; and we have seen that events are fired
+when a principal is added to an already-registered groups. Events are also
+fired when a principal is removed from an already-registered groups. Let's
+quickly see some more examples.
+
+ >>> g1.principals = (p1.__name__, p3.__name__, p4.__name__)
+ >>> g1.principals
+ (..., ..., ...)
+
+ >>> g1.principals[0] == p1.__name__
+ True
+
+ >>> g1.principals[1] == p3.__name__
+ True
+
+ >>> g1.principals[2] == p4.__name__
+ True
+
+ >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
+ <PrincipalsAddedToGroup [..., ...] u'groups.G1'>
+
+ >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
+ <PrincipalsRemovedFromGroup [...] u'groups.G1'>
+
+ >>> g1.principals = (p1.__name__, p2.__name__)
+ >>> g1.principals
+ (..., ...)
+
+ >>> g1.principals[0] == p1.__name__
+ True
+
+ >>> g1.principals[1] == p2.__name__
+ True
+
+ >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
+ <PrincipalsAddedToGroup [...] u'groups.G1'>
+
+ >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
+ <PrincipalsRemovedFromGroup [..., ...] u'groups.G1'>
+
+ >>> groups.getGroupsForPrincipal(p2.__name__)
+ (u'groups.G1',)
+
+
+Groups in groups
+----------------
+
+Groups can contain groups:
+
+ >>> g2 = Group('Group Two')
+ >>> groups['groups.G2'] = g2
+ >>> g2.principals
+ ()
+
+ >>> g2.principals = ['groups.G1']
+
+ >>> g2.principals
+ ('groups.G1',)
+
+ >>> groups.getGroupsForPrincipal('groups.G2')
+ ()
+
+ >>> g1.principals
+ (..., ...)
+
+ >>> g1.principals[0] == p1.__name__
+ True
+
+ >>> g1.principals[1] == p2.__name__
+ True
+
+ >>> groups.getGroupsForPrincipal('groups.G1')
+ (u'groups.G2',)
+
+ >>> old = getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
+ >>> old
+ <PrincipalsAddedToGroup ['groups.G1'] u'groups.G2'>
+
+Groups cannot contain cycles:
+
+ >>> g1.principals
+ (..., ...)
+
+ >>> g1.principals[0] == p1.__name__
+ True
+
+ >>> g1.principals[1] == p2.__name__
+ True
+
+ >>> g2.principals
+ ('groups.G1',)
+
+ >>> g1.principals = (p1.__name__, p2.__name__, 'groups.G2')
+ Traceback (most recent call last):
+ ...
+ GroupCycle: (...)
+
+ >>> g1.principals
+ (..., ...)
+
+ >>> g1.principals[0] == p1.__name__
+ True
+
+ >>> g1.principals[1] == p2.__name__
+ True
+
+Trying to do so does not fire an event.
+
+ >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1] is old
+ True
+
+They need not be hierarchical:
+
+ >>> ga = Group("Group A")
+ >>> groups['groups.GA'] = ga
+
+ >>> gb = Group("Group B")
+ >>> groups['groups.GB'] = gb
+ >>> gb.principals = ['groups.GA']
+
+ >>> gc = Group("Group C")
+ >>> groups['groups.GC'] = gc
+ >>> gc.principals = ['groups.GA']
+
+ >>> gd = Group("Group D")
+ >>> groups['groups.GD'] = gd
+ >>> gd.principals = ['groups.GA', 'groups.GB']
+
+ >>> ga.principals = [p1.__name__]
+
+Group containers provide a very simple search interface. They perform
+simple string searches on group titles and descriptions.
+
+ >>> list(groups.search({'search': 'grou'}))
+ [u'groups.G1', u'groups.G2',
+ u'groups.GA', u'groups.GB', u'groups.GC', u'groups.GD']
+
+ >>> list(groups.search({'search': 'two'}))
+ [u'groups.G2']
+
+They also support batching:
+
+ >>> list(groups.search({'search': 'grou'}, 2, 3))
+ [u'groups.GA', u'groups.GB', u'groups.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:
+
+ >>> auth1 = sau.getPrincipal(p1.__name__)
+
+ >>> auth1.groups
+ [u'groups.G1', u'groups.GA']
+
+Of course, this applies to groups too:
+
+ >>> g1 = sau.getPrincipal('groups.G1')
+ >>> g1.id
+ u'groups.G1'
+
+ >>> g1.groups
+ [u'groups.G2']
+
+A GroupPrincipal provides IGroupPrincipal which is inherited from
+IFoundPrincipal and IGroup:
+
+ >>> interfaces.IGroupPrincipal.providedBy(g1)
+ True
+
+ >>> interfaces.IFoundPrincipal.providedBy(g1)
+ True
+
+ >>> import zope.security.interfaces
+ >>> zope.security.interfaces.IGroup.providedBy(g1)
+ True
+
+
+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
+users if IAuthenticatedGroup, or IEveryoneGroup utilities are
+provided.
+
+If we notify the subscriber with the principal, nothing will happen
+because the groups haven't been defined:
+
+ >>> from z3c.authenticator.principal import FoundPrincipal
+ >>> from z3c.authenticator.event import FoundPrincipalCreated
+ >>> from z3c.authenticator.group import specialGroups
+ >>> x = User('x', 'password', 'X')
+ >>> found = FoundPrincipal(x)
+ >>> event = FoundPrincipalCreated(sau, found)
+ >>> specialGroups(event)
+ >>> found.groups
+ []
+
+Now, if we define the Everybody group:
+
+ >>> import zope.app.security.interfaces
+ >>> class EverybodyGroup(GroupPrincipal):
+ ... zope.interface.implements(
+ ... zope.app.security.interfaces.IEveryoneGroup)
+
+ >>> all = Group('groups.all')
+ >>> groups['groups.all'] = all
+ >>> everybody = EverybodyGroup(all)
+ >>> zope.component.provideUtility(everybody,
+ ... zope.app.security.interfaces.IEveryoneGroup)
+
+Then the group will be added to the principal:
+
+ >>> specialGroups(event)
+ >>> found.groups
+ [u'groups.all']
+
+Similarly for the authenticated group:
+
+ >>> class AuthenticatedGroup(GroupPrincipal):
+ ... zope.interface.implements(
+ ... zope.app.security.interfaces.IAuthenticatedGroup)
+
+ >>> authenticated = Group('groups.authenticated')
+ >>> groups['groups.authenticated'] = authenticated
+ >>> authenticated = AuthenticatedGroup(authenticated)
+ >>> zope.component.provideUtility(authenticated,
+ ... zope.app.security.interfaces.IAuthenticatedGroup)
+
+Then the group will be added to the principal:
+
+ >>> found.groups = []
+ >>> specialGroups(event)
+ >>> found.groups.sort()
+ >>> found.groups
+ [u'groups.all', u'groups.authenticated']
+
+These groups are only added to non-group principals:
+
+ >>> found.groups = []
+ >>> zope.interface.directlyProvides(found, zope.security.interfaces.IGroup)
+ >>> specialGroups(event)
+ >>> found.groups
+ []
+
+And they are only added to group aware principals:
+
+ >>> class SolitaryPrincipal:
+ ... zope.interface.implements(zope.security.interfaces.IPrincipal)
+ ... id = title = description = ''
+
+ >>> event = FoundPrincipalCreated(sau, SolitaryPrincipal())
+ >>> specialGroups(event)
+ >>> found.groups
+ []
+
+
+UserAwareGroup
+----------------
+
+We can manages groups via the group getUser and setUser methods:
+
+ >>> foundGroup = interfaces.IFoundPrincipal(g1)
+ >>> foundGroup.getUsers()
+ (..., ...)
+
+ >>> foundGroup.getUsers()[0] == p1.__name__
+ True
+
+ >>> foundGroup.getUsers()[1] == p2.__name__
+ True
+
+Let's remove a member from the group:
+
+ >>> foundGroup.setUsers((p2.__name__,))
+ >>> foundGroup.getUsers()
+ (...,)
+
+ >>> foundGroup.getUsers()[0] == p2.__name__
+ True
+
+ >>> foundGroup.members
+ (...,)
+
+ >>> foundGroup.getUsers()[0] == p2.__name__
+ True
+
+Ensure that such a group provides the IMemberAwareGroup interfaces.
+
+ >>> zope.security.interfaces.IMemberAwareGroup.providedBy(foundGroup)
+ True
+
+#User-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 groups...
+#
+# >>> 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.setUserSubscriber(
+# ... interfaces.FoundPrincipalCreated(
+# ... 'dummy auth (ignored)', principal, info))
+# >>> principal.getUsers()
+# ('joe', 'jane')
+# >>> principal.setUsers(('joe', 'jane', 'jaimie'))
+# >>> principal.getUsers()
+# ('joe', 'jane', 'jaimie')
+# >>> zope.security.interfaces.IMemberAwareGroup.providedBy(principal)
+# True
+#
+#The two methods work with the value on the IGroupInformation object.
+#
+# >>> i.principals == principal.getUsers()
+# True
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/group.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/group.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/group.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/group.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,50 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="z3c">
+
+ <class class=".group.Group">
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IGroup"
+ />
+ <require
+ permission="zope.ManageServices"
+ set_schema=".interfaces.IGroup"
+ />
+ </class>
+
+ <class class=".group.GroupContainer">
+ <implements
+ interface=".interfaces.IGroupContainer"
+ />
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IGroupContainer
+ zope.app.container.interfaces.INameChooser"
+ />
+ </class>
+
+ <adapter
+ factory=".group.GroupPrincipal"
+ />
+
+ <adapter
+ provides="zope.app.container.interfaces.INameChooser"
+ for=".interfaces.IGroupContainer"
+ factory="zope.app.authentication.idpicker.IdPicker"
+ />
+
+ <subscriber
+ for=".interfaces.IPrincipalCreated"
+ handler=".group.specialGroups"
+ />
+
+ <subscriber
+ for=".interfaces.IPrincipalCreated"
+ handler=".group.setGroupsForPrincipal"
+ />
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/group.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,473 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.schema
+
+from zope.security.interfaces import IGroupClosureAwarePrincipal
+from zope.security.interfaces import IMemberAwareGroup
+from zope.app.authentication.interfaces import ICredentialsPlugin
+from zope.app.container.interfaces import IContainer
+from zope.app.container.constraints import contains
+from zope.app.container.constraints import containers
+from zope.app.security.interfaces import ILogout
+from zope.app.security.vocabulary import PrincipalSource
+
+from z3c.i18n import MessageFactory as _
+
+
+class IPlugin(zope.interface.Interface):
+ """A plugin for IAuthenticator component."""
+
+
+# authenticator interfaces
+class IAuthenticatorPlugin(IPlugin):
+ """Authenticates and provides a principal using credentials."""
+
+ containers('z3c.authenticator.interfaces.IAuthenticator')
+
+ def authenticateCredentials(credentials):
+ """Authenticates credentials and return a IPrincipal object.
+
+ If the credentials can be authenticated, return an object that provides
+ IPrincipal. If the plugin cannot authenticate the credentials,
+ returns None.
+ """
+
+ def queryPrincipal(id, default=None):
+ """Returns an IPrincipal object for the given principal id or default.
+
+ If the plugin cannot find information for the id, returns None.
+ """
+
+
+class ICredentialsPlugin(IPlugin):
+ """Handles credentials extraction and challenges per request."""
+
+ containers('z3c.authenticator.interfaces.IAuthenticator')
+
+ 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 IAuthenticator(ILogout, IContainer):
+ """Authentication utility.
+
+ The Authenticator supports NOT IAuthenticatorPlugin plugins defined
+ in zope.app.authentication.interfaces. Because they use and return a
+ IPrincipalInfo object in the authenticateCredentials method.
+
+ Note: you have to write your own authenticator plugins because we do not
+ use the IPrincipalInfo implementation in this authentication module.
+ """
+
+ 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 IAuthenticator, or names of registered
+ ICredentialsPlugins utilities. Contained non-utility ids mask
+ utility names."""),
+ value_type=zope.schema.Choice(vocabulary='Z3CCredentialsPlugins'),
+ 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 IAuthenticator, or names of registered
+ IAuthenticatorPlugins utilities. Contained non-utility ids mask
+ utility names."""),
+ value_type=zope.schema.Choice(vocabulary='Z3CAuthenticatorPlugins'),
+ 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.
+ """
+
+ def logout(request):
+ """Performs a logout by delegating to its authenticator plugins."""
+
+
+
+# user interfaces
+class IUser(zope.interface.Interface):
+ """User"""
+
+ containers('z3c.authenticator.interfaces.IUserContainer')
+
+ login = zope.schema.TextLine(
+ title=_("Login"),
+ description=_("The Login/Username of the principal. "
+ "This value can change."))
+
+ password = zope.schema.Password(
+ title=_("Password"),
+ description=_("The password for the principal."))
+
+ passwordManagerName = zope.schema.Choice(
+ title=_("Password Manager"),
+ vocabulary="Password Manager Names",
+ description=_("The password manager will be used"
+ " for encode/check the password"),
+ default="Plain Text",
+ # TODO: The password manager name may be changed only
+ # if the password changed
+ readonly=True
+ )
+
+ title = zope.schema.TextLine(
+ title=_("Title"),
+ description=_("Provides a title for the principal."))
+
+ description = zope.schema.Text(
+ title=_("Description"),
+ description=_("Provides a description for the principal."),
+ required=False,
+ missing_value='',
+ default=u'')
+
+
+class IUserSearchCriteria(zope.interface.Interface):
+ """Search Interface for this Principal Provider"""
+
+ search = zope.schema.TextLine(
+ title=_("Search String"),
+ description=_("A Search String"),
+ required=False,
+ default=u'',
+ missing_value=u'')
+
+
+class IUserContainer(IContainer, IAuthenticatorPlugin):
+ """Principal container."""
+
+ contains(IUser)
+
+
+# principal interfaces
+class IFoundPrincipal(IGroupClosureAwarePrincipal):
+ """A factory adapting IUser and offering read access to the principal.
+
+ A found principal gets created by the IAuthenticators search method
+ for users matching the search critaria.
+ """
+
+ id = zope.interface.Attribute("The principal id.")
+
+ title = zope.interface.Attribute("The principal title.")
+
+ description = zope.interface.Attribute("A description of the principal.")
+
+ groups = zope.schema.List(
+ title=_("Groups"),
+ description=_(
+ """Ids of groups to which the user 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 container in this package.)
+ """),
+ value_type=zope.schema.TextLine(),
+ required=False)
+
+
+class IAuthenticatedPrincipal(IGroupClosureAwarePrincipal):
+ """A factory adapting IInternalPrincipal and offering read access to the
+ principal.
+
+ A authenticated principal gets created by the IAuthenticators
+ authenticateCredentials method for principals matching the credential
+ criteria.
+ """
+
+ id = zope.interface.Attribute("The principal id.")
+
+ title = zope.interface.Attribute("The principal title.")
+
+ description = zope.interface.Attribute("A description of the principal.")
+
+ groups = zope.schema.List(
+ title=_("Groups"),
+ description=_(
+ """Ids of groups to which the user 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 container in this package.)
+ """),
+ value_type=zope.schema.TextLine(),
+ required=False)
+
+
+# group interfaces
+class IGroup(zope.interface.Interface):
+
+ containers('z3c.authenticato.interfaces.IGroupContainer')
+
+ title = zope.schema.TextLine(
+ title=_("Title"),
+ description=_("Provides a title for the permission."),
+ required=True)
+
+ description = zope.schema.Text(
+ title=_("Description"),
+ description=_("Provides a description for the permission."),
+ required=False)
+
+ principals = zope.schema.List(
+ title=_("Principals"),
+ value_type=zope.schema.Choice(
+ source=PrincipalSource()),
+ description=_(
+ "List of ids of principals which belong to the group"),
+ required=False)
+
+
+class IGroupContainer(IContainer, IAuthenticatorPlugin):
+
+ contains(IGroup)
+
+ prefix = zope.schema.TextLine(
+ title=_('Prefix'),
+ description=_("Prefix added to IDs of groups in this container"),
+ default=u'',
+ required=True,
+ readonly=True,
+ )
+
+ def getGroupsForPrincipal(principalid):
+ """Get groups the given principal belongs to"""
+
+ def getPrincipalsForGroup(groupid):
+ """Get principals which belong to the group"""
+
+
+class IGroupSearchCriteria(zope.interface.Interface):
+
+ search = zope.schema.TextLine(
+ title=_("Group Search String"),
+ required=False,
+ missing_value=u'',
+ )
+
+
+class IGroupPrincipal(IFoundPrincipal, IMemberAwareGroup):
+ """IGroup that acts as a principal representing a group."""
+
+ members = zope.interface.Attribute('an iterable of members of the group')
+
+
+# principal event interfaces
+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")
+
+
+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 IFoundPrincipalCreated(IPrincipalCreated):
+ """A principal has been created by way of a search operation."""
+
+
+# group event interfaces
+class IGroupAdded(zope.interface.Interface):
+ """A group has been added."""
+
+ group = zope.interface.Attribute("""The group that was defined""")
+
+
+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')
+
+
+# credential
+class IHTTPBasicAuthRealm(zope.interface.Interface):
+ """HTTP Basic Auth Realm
+
+ Represents the realm string that is used during basic HTTP authentication
+ """
+
+ realm = zope.schema.TextLine(
+ title=u'Realm',
+ description=u'HTTP Basic Authentication Realm',
+ required=True,
+ default=u'ZAM')
+
+
+class ISessionCredentials(zope.interface.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 IBrowserFormChallenger(zope.interface.Interface):
+ """A challenger that uses a browser form to collect user credentials."""
+
+ loginpagename = zope.schema.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 = zope.schema.TextLine(
+ title=u'Loginfield',
+ description=u"Field of the login page in which is looked for the login user name.",
+ default=u"login")
+
+ passwordfield = zope.schema.TextLine(
+ title=u'Passwordfield',
+ description=u"Field of the login page in which is looked for the password.",
+ default=u"password")
+
+
+class IHTTPBasicAuthCredentialsPlugin(ICredentialsPlugin, IHTTPBasicAuthRealm):
+ """BAsic authentication credential plugin."""
+
+
+class ISessionCredentialsPlugin(ICredentialsPlugin, IBrowserFormChallenger):
+ """Session credential plugin."""
+
+
+class ILoginSchema(zope.interface.Interface):
+ """The subscription form."""
+
+ login = zope.schema.TextLine(
+ title=_(u'Login'),
+ description=_(u'Your login name.'),
+ required=True)
+
+ password = zope.schema.Password(
+ title=_(u'Password'),
+ description=_(u'Your password.'))
+
+ camefrom = zope.schema.TextLine(
+ title=_(u'Camefrom'),
+ description=_(u'The url which the user came form.'))
+
+
+# queriable search interfaces
+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.
+ """
+
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/principal.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/principal.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/principal.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+from zope.app.security.interfaces import IAuthentication
+
+from z3c.authenticator import interfaces
+
+
+class PrincipalBase(object):
+ """Base class for IAuthenticatedPrincipal and IFoundPrincipal principals.
+ """
+
+ title = None
+
+ def __init__(self, principal):
+ """We offer no access to the principal object itself."""
+ self.id = principal.__name__
+ self.title = principal.title
+ self.description = principal.description
+ self.groups = []
+
+ @property
+ def allGroups(self):
+ if self.groups:
+ seen = set()
+ auth = zope.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 = auth.getPrincipal(group_id)
+ stack.append(iter(group.groups))
+
+ def __repr__(self):
+ return "<%s %s>" %(self.__class__.__name__, self.id)
+
+
+class AuthenticatedPrincipal(PrincipalBase):
+ """Default IAuthenticatedPrincipal principal."""
+
+ zope.component.adapts(interfaces.IUser)
+
+ zope.interface.implements(interfaces.IAuthenticatedPrincipal)
+
+
+class FoundPrincipal(PrincipalBase):
+ """Default IFoundPrincipal principal."""
+
+ zope.component.adapts(interfaces.IUser)
+
+ zope.interface.implements(interfaces.IFoundPrincipal)
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/principal.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,9 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="z3c">
+
+ <adapter factory=".principal.FoundPrincipal" />
+
+ <adapter factory=".principal.AuthenticatedPrincipal" />
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/testing.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/testing.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/testing.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.component
+from zope.app.authentication.interfaces import IPasswordManager
+from zope.app.authentication.password import PlainTextPasswordManager
+from zope.app.testing import setup
+
+###############################################################################
+#
+# setup helper
+#
+###############################################################################
+
+def setUpPasswordManager():
+ zope.component.provideUtility(PlainTextPasswordManager(),
+ IPasswordManager, "Plain Text")
+
+
+###############################################################################
+#
+# placeful setup/teardown
+#
+###############################################################################
+
+def siteSetUp(test):
+ site = setup.placefulSetUp(site=True)
+ test.globs['rootFolder'] = site
+
+
+def siteTearDown(test):
+ setup.placefulTearDown()
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/testing.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/tests.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/tests.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/tests.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,174 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import unittest
+from zope.testing import doctest
+from zope.app.testing import placelesssetup
+
+from z3c.testing import InterfaceBaseTest
+from z3c.testing import BaseTestIContainer
+from z3c.authenticator import interfaces
+from z3c.authenticator import authentication
+from z3c.authenticator import credential
+from z3c.authenticator import group
+from z3c.authenticator import user
+from z3c.authenticator import principal
+from z3c.authenticator import testing
+
+
+class AuthenticatorTest(BaseTestIContainer):
+
+ def getTestInterface(self):
+ return interfaces.IAuthenticator
+
+ def getTestClass(self):
+ return authentication.Authenticator
+
+
+class UserContainerTest(InterfaceBaseTest):
+
+ def getTestInterface(self):
+ return interfaces.IUserContainer
+
+ def getTestClass(self):
+ return user.UserContainer
+
+
+class UserTest(InterfaceBaseTest):
+
+ def setUp(self):
+ testing.setUpPasswordManager()
+
+ def getTestInterface(self):
+ return interfaces.IUser
+
+ def getTestClass(self):
+ return user.User
+
+ def getTestPos(self):
+ return (u'login', u'password', u'Title')
+
+
+class AuthenticatedPrincipalTest(InterfaceBaseTest):
+
+ def setUp(self):
+ testing.setUpPasswordManager()
+
+ def getTestInterface(self):
+ return interfaces.IAuthenticatedPrincipal
+
+ def getTestClass(self):
+ return principal.AuthenticatedPrincipal
+
+ def makeTestObject(self):
+ usr = user.User(u'login', u'password', u'Title')
+ return principal.AuthenticatedPrincipal(usr)
+
+
+class FoundPrincipalTest(InterfaceBaseTest):
+
+ def setUp(self):
+ testing.setUpPasswordManager()
+
+ def getTestInterface(self):
+ return interfaces.IFoundPrincipal
+
+ def getTestClass(self):
+ return principal.FoundPrincipal
+
+ def makeTestObject(self):
+ usr = user.User(u'login', u'password', u'Title')
+ return principal.FoundPrincipal(usr)
+
+
+class GroupContainerTest(InterfaceBaseTest):
+
+ def getTestInterface(self):
+ return interfaces.IGroupContainer
+
+ def getTestClass(self):
+ return group.GroupContainer
+
+
+class GroupTest(InterfaceBaseTest):
+
+ def getTestInterface(self):
+ return interfaces.IGroup
+
+ def getTestClass(self):
+ return group.Group
+
+
+class SessionCredentialsTest(InterfaceBaseTest):
+
+ def getTestInterface(self):
+ return interfaces.ISessionCredentials
+
+ def getTestClass(self):
+ return credential.SessionCredentials
+
+ def getTestPos(self):
+ return (u'login', u'password')
+
+
+class SessionCredentialsPluginTest(InterfaceBaseTest):
+
+ def getTestInterface(self):
+ return interfaces.ICredentialsPlugin
+
+ def getTestClass(self):
+ return credential.SessionCredentialsPlugin
+
+
+class SessionCredentialsPluginFormTest(InterfaceBaseTest):
+
+ def getTestInterface(self):
+ return interfaces.IBrowserFormChallenger
+
+ def getTestClass(self):
+ return credential.SessionCredentialsPlugin
+
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('README.txt',
+ setUp=testing.siteSetUp, tearDown=testing.siteTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+ doctest.DocFileSuite('group.txt',
+ setUp=testing.siteSetUp, tearDown=testing.siteTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+ doctest.DocTestSuite('z3c.authenticator.credential',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown),
+ doctest.DocTestSuite('z3c.authenticator.group',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown),
+ doctest.DocFileSuite('vocabulary.txt',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown),
+ unittest.makeSuite(AuthenticatorTest),
+ unittest.makeSuite(UserContainerTest),
+ unittest.makeSuite(UserTest),
+ unittest.makeSuite(AuthenticatedPrincipalTest),
+ unittest.makeSuite(FoundPrincipalTest),
+ unittest.makeSuite(GroupContainerTest),
+ unittest.makeSuite(GroupTest),
+ unittest.makeSuite(SessionCredentialsTest),
+ unittest.makeSuite(SessionCredentialsPluginTest),
+ unittest.makeSuite(SessionCredentialsPluginFormTest),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/tests.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/user.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/user.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/user.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,255 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import md5
+import random
+import time
+import socket
+import persistent
+import zope.interface
+import zope.component
+from zope.app.container import contained
+from zope.app.container import btree
+from zope.app.container.interfaces import DuplicateIDError
+from zope.app.authentication.interfaces import IPasswordManager
+
+from z3c.authenticator import interfaces
+
+# get the IP addresss only once
+try:
+ ip = socket.getaddrinfo(socket.gethostname(), 0)[-1][-1][0]
+except:
+ ip = '127.0.0.1'
+
+def generateUserIDToken(id):
+ """Generates a unique user id token."""
+ t = long(time.time() * 1000)
+ r = long(random.random()*100000000000000000L)
+ data = str(ip)+' '+str(t)+' '+str(r)+' '+str(id)
+ return unicode(md5.md5(data).hexdigest())
+
+
+class User(persistent.Persistent, contained.Contained):
+ """User stored in IUserContainer."""
+
+ zope.interface.implements(interfaces.IUser)
+
+ def __init__(self, login, password, title, description=u'',
+ passwordManagerName="Plain Text"):
+ 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 zope.component.getUtility(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 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 UserContainer(btree.BTreeContainer):
+ """A Persistent User Container and authenticator plugin.
+
+ See principalfolder.txt for details.
+ """
+
+ zope.interface.implements(interfaces.IUserContainer,
+ interfaces.IQuerySchemaSearch)
+
+ schema = interfaces.IUserSearchCriteria
+
+ def __init__(self):
+ super(UserContainer, 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, user):
+ """Add a IPrincipal object within a correct id.
+
+ Create a UserContainer
+
+ >>> mc = UserContainer('users.')
+
+ Try to add something not providing IUser
+ >>> try:
+ ... mc.__setitem__(u'max', object())
+ ... except Exception, e:
+ ... print e
+ Item does not support IUser!
+
+ Create a user with no __name__, this should be added via the add
+ method
+
+ >>> user = User()
+ >>> try:
+ ... mc.__setitem__(u'max', user)
+ ... except Exception, e:
+ ... print e
+ There is no user id token given!
+
+ Probabl we do hav a __name__ during copy/paste, so we have to check
+ if we get a __parent__ as well
+
+ >>> user = User()
+ >>> user.__name__ = u'usertoken'
+ >>> try:
+ ... mc.__setitem__(u'max', user)
+ ... except Exception, e:
+ ... print e
+ Paste a object is not supported!
+
+ Try to use a user with no login:
+
+ >>> try:
+ ... mc.__setitem__(u'max', user)
+ ... except Exception, e:
+ ... print e
+ User does not provide a login value!
+
+ Add a login attr since __setitem__ is in need of one
+
+ >>> user.login = u'max'
+ >>> mc.__setitem__(u'users.max', principal)
+
+ Now try to use the same login:
+
+ >>> try:
+ ... mc.__setitem__(u'max', user)
+ ... except Exception, e:
+ ... print e
+ Login already taken!
+ """
+
+ # check if we store correct implementations
+ if not interfaces.IUser.providedBy(user):
+ raise TypeError('Item does not support IUser!')
+
+ # check if there is a user id given
+ if user.__name__ is not id:
+ raise ValueError('There is no user id token given!')
+
+ # check if there is a user id given in we store correct implementations
+ if user.__parent__ is not None:
+ raise ValueError('Paste a object is not supported!')
+
+ # The user doesn't provide a login
+ if not user.login:
+ raise ValueError('User does not provide a login value!')
+
+ # A user with the new login already exists
+ if user.login in self.__id_by_login:
+ raise DuplicateIDError('Login already taken!')
+
+ super(UserContainer, self).__setitem__(id, user)
+ self.__id_by_login[user.login] = id
+
+ def add(self, user):
+ token = generateUserIDToken(user.login)
+ # Pre set the user id like a ticket, so we can check it in __setitem__
+ user.__name__ = token
+ self[token] = user
+ return token, self[token]
+
+ def __delitem__(self, id):
+ """Remove principal information."""
+ user = self[id]
+ super(UserContainer, self).__delitem__(id)
+ del self.__id_by_login[user.login]
+
+ def authenticateCredentials(self, credentials):
+ """Return principal 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
+ user = self[id]
+ if not user.checkPassword(credentials["password"]):
+ return None
+ return user
+
+ def queryPrincipal(self, id, default=None):
+ user = self.get(id)
+ if user is not None:
+ return user
+ return default
+
+ 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 value.__name__
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/user.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/user.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/user.zcml (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/user.zcml 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,23 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="z3c">
+
+ <class class=".user.User">
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IUser"
+ set_schema=".interfaces.IUser"
+ />
+ </class>
+
+ <class class=".user.UserContainer">
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IUserContainer"
+ />
+ </class>
+
+</configure>
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/user.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.py (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.py 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation 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.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import zope.interface
+import zope.component
+import zope.i18n
+import zope.dublincore.interfaces
+from zope.schema import vocabulary
+from zope.schema.interfaces import IVocabularyFactory
+
+from z3c.i18n import MessageFactory as _
+from z3c.authenticator import interfaces
+
+UTILITY_TITLE = _(
+ 'z3c.authenticator.vocabulary-utility-plugin-title',
+ '${name} (a utility)')
+CONTAINED_TITLE = _(
+ 'z3c.authenticator.vocabulary-contained-plugin-title',
+ '${name} (in contents)')
+MISSING_TITLE = _(
+ 'z3c.authenticator.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 Authenticator).
+
+ These plugins may be objects contained within the Authenticator
+ ("contained plugins"), or may be utilities registered for the specified
+ interface, found in the context of the Authenticator
+ ("utility plugins"). Contained plugins mask utility plugins of the same
+ name.
+
+ The vocabulary also includes the current values of the
+ Authenticator even if they do not correspond to a contained or
+ utility plugin.
+ """
+ terms = {}
+ auth = interfaces.IAuthenticator.providedBy(context)
+ if auth:
+ 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(), zope.i18n.Message(
+ CONTAINED_TITLE, mapping={'name': title}))
+ utils = zope.component.getUtilitiesFor(interface, context)
+ for nm, util in utils:
+ if nm not in terms:
+ terms[nm] = vocabulary.SimpleTerm(
+ nm, nm.encode('base64').strip(), zope.i18n.Message(
+ UTILITY_TITLE, mapping={'name': nm}))
+ if auth:
+ for nm in set(getattr(context, attr_name)):
+ if nm not in terms:
+ terms[nm] = vocabulary.SimpleTerm(
+ nm, nm.encode('base64').strip(), zope.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')
+
+zope.interface.alsoProvides(authenticatorPlugins, IVocabularyFactory)
+
+def credentialsPlugins(context):
+ return _pluginVocabulary(
+ context, interfaces.ICredentialsPlugin, 'credentialsPlugins')
+
+zope.interface.alsoProvides(credentialsPlugins, IVocabularyFactory)
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt 2008-03-23 11:03:15 UTC (rev 84862)
@@ -0,0 +1,136 @@
+==========
+Vocabulary
+==========
+
+The vocabulary module provides vocabularies for the authenticatorPlugins.
+
+The options should include the unique names of all of the plugins that provide
+the appropriate interface (interfaces.IAuthentiatorPlugin, respectively) for
+the current context-- which is expected to be a pluggable authentication
+utility, hereafter referred to as a Authenticator.
+
+These names may be for objects contained within the Authenticator
+("contained plugins"), or may be utilities registered for the specified
+interface, found in the context of the Authenticator
+("utility plugins"). Contained plugins mask utility plugins of the same name.
+They also may be names currently selected in the Authenticator 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 Authenticator.
+
+ >>> from z3c.authenticator import interfaces
+ >>> import zope.interface
+ >>> import zope.component
+ >>> class DemoPlugin(object):
+ ... zope.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():
+ ... zope.component.provideUtility(p, name=p.name)
+ ...
+ >>> sorted(contained_plugins.keys()) # 1 will mask utility plugin 1
+ [1, 2, 3, 4]
+ >>> class DemoAuth(dict):
+ ... zope.interface.implements(interfaces.IAuthenticator)
+ ... 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())
+
+ >>> @zope.component.adapter(zope.interface.Interface)
+ ... @zope.interface.implementer(zope.component.IComponentLookup)
+ ... def getSiteManager(context):
+ ... return zope.component.getGlobalSiteManager()
+ ...
+ >>> zope.component.provideAdapter(getSiteManager)
+
+We are now ready to create a vocabulary that we can use. The context is
+our faux authentication utility, `auth`.
+
+ >>> from z3c.authenticator 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(zope.interface.Interface):
+ ... pass
+ ...
+ >>> zope.interface.directlyProvides(contained_plugins[1], ISpecial)
+ >>> class DemoDCAdapter(object):
+ ... zope.interface.implements(
+ ... zope.dublincore.interfaces.IDCDescriptiveProperties)
+ ... zope.component.adapts(ISpecial)
+ ... def __init__(self, context):
+ ... pass
+ ... title = u'Special Title'
+ ...
+ >>> zope.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)']
Property changes on: z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list