[Checkins]
SVN: z3c.authentication/trunk/src/z3c/authentication/simple/
Added simple authentication implementation
Roger Ineichen
roger at projekt01.ch
Wed Jan 17 20:27:11 EST 2007
Log message for revision 72089:
Added simple authentication implementation
Changed:
A z3c.authentication/trunk/src/z3c/authentication/simple/
A z3c.authentication/trunk/src/z3c/authentication/simple/README.txt
A z3c.authentication/trunk/src/z3c/authentication/simple/SETUP.cfg
A z3c.authentication/trunk/src/z3c/authentication/simple/TODO.txt
A z3c.authentication/trunk/src/z3c/authentication/simple/__init__.py
A z3c.authentication/trunk/src/z3c/authentication/simple/authentication.py
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/__init__.py
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/add.py
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/adding.py
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/configure.zcml
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/edit.py
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/group.zcml
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/member.zcml
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/register.py
A z3c.authentication/trunk/src/z3c/authentication/simple/browser/schemasearch.py
A z3c.authentication/trunk/src/z3c/authentication/simple/configure.zcml
A z3c.authentication/trunk/src/z3c/authentication/simple/event.py
A z3c.authentication/trunk/src/z3c/authentication/simple/group.py
A z3c.authentication/trunk/src/z3c/authentication/simple/group.txt
A z3c.authentication/trunk/src/z3c/authentication/simple/group.zcml
A z3c.authentication/trunk/src/z3c/authentication/simple/interfaces.py
A z3c.authentication/trunk/src/z3c/authentication/simple/member.py
A z3c.authentication/trunk/src/z3c/authentication/simple/member.zcml
A z3c.authentication/trunk/src/z3c/authentication/simple/principal.py
A z3c.authentication/trunk/src/z3c/authentication/simple/principal.zcml
A z3c.authentication/trunk/src/z3c/authentication/simple/tests.py
A z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.py
A z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.txt
A z3c.authentication/trunk/src/z3c/authentication/simple/z3c.authentication.simple-configure.zcml
-=-
Added: z3c.authentication/trunk/src/z3c/authentication/simple/README.txt
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/README.txt 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/README.txt 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,451 @@
+=============================
+Simple Authentication Utility
+=============================
+
+The Simple 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 IMember implementations.
+
+
+Security
+--------
+
+The SimpleAuthentication 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.
+Beacuse 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 IMember. This adapters do not offer
+it's context which is the real IMember.
+
+The SimpleAuthentication doesn't use a prefix. The usage of a prefix is only
+implemented in the IMemberContainer.
+
+We do not use a prefix in the IMemberContainer 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 IMember instances anymore. We heavily restricted the
+usage of this method. See the inline doc tests in __setitem__ for more info.
+
+The ICredentialsPlugin used in the IPluggableAuthentication are 100%
+compatible with our SimpleAuthentication implementation.
+
+
+Authentication
+==============
+
+The primary job of SimpleAuthentication 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 SimpleAuthentication returns a principal object,
+if it can. The SimpleAutentication 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
+SimpleAuthentication 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.authentication.simple import interfaces
+ >>> from z3c.authentication.simple.member import Member
+ >>> login = u'bob'
+ >>> password = u'secret'
+ >>> title = u'Bob'
+ >>> p = Member(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 IMember:
+
+ >>> interfaces.IMember.providedBy(p)
+ True
+
+
+Authenticator Plugin
+--------------------
+
+First setup a MemberContainer which will store the principals:
+
+ >>> from z3c.authentication.simple.member import MemberContainer
+ >>> authPlugin = MemberContainer()
+
+Now we have a MemberContainer that provides a IMemberContainer:
+
+ >>> interfaces.IMemberContainer.providedBy(authPlugin)
+ True
+
+Now we will add the created principal to the principal container using the
+containers method ``add``:
+
+ >>> uid, member = authPlugin.add(p)
+
+The method returns the member id and the member object. The id get generated
+by the host IP address, the time, a random string and the member 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.
+
+ >>> member.__name__ == uid
+ True
+
+The returned member still is our previous added IMember
+
+ >>> member is p
+ True
+
+ >>> len(member.__name__)
+ 32
+
+ >>> member.login
+ u'bob'
+
+ >>> member.password
+ u'secret'
+
+ >>> member.title
+ u'Bob'
+
+let's register the MemberContainer as a named IAuthenticatorPlugin
+utility:
+
+ >>> import zope.component
+ >>> zope.component.provideUtility(authPlugin,
+ ... provides=interfaces.IAuthenticatorPlugin,
+ ... name='My Authenticator Plugin')
+
+
+Credentials Plugin
+------------------
+
+After setup the member and member container, we'll create a simple
+credentials plugin:
+
+ >>> import zope.interface
+ >>> import zope.component
+ >>> from zope.app.authentication.interfaces import ICredentialsPlugin
+
+ >>> class MyCredentialsPlugin(object):
+ ...
+ ... zope.interface.implements(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 SimpleAuthentication 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 member, they are not responsible
+for creating principals. This function is performed by the
+SimpleAuthentication:
+
+ >>> from z3c.authentication.simple.principal import AuthenticatedPrincipal
+ >>> from z3c.authentication.simple.principal import FoundPrincipal
+ >>> zope.component.provideAdapter(AuthenticatedPrincipal,
+ ... provides=interfaces.IAuthenticatedPrincipal)
+
+ >>> zope.component.provideAdapter(FoundPrincipal,
+ ... provides=interfaces.IFoundPrincipal)
+
+
+Configuring SimpleAuthentication
+--------------------------------
+
+Finally, we'll create the SimpleAuthentication itself:
+
+ >>> from z3c.authentication.simple import authentication
+ >>> sau = authentication.SimpleAuthentication()
+
+and configure it with the two plugins:
+
+ >>> sau.credentialsPlugins = ('My Credentials Plugin', )
+ >>> sau.authenticatorPlugins = ('My Authenticator Plugin', )
+
+
+Authenticate
+------------
+
+We can now use the SimpleAuthentication to authenticate a sample request:
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> print sau.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 sau.authenticate(request)
+ None
+
+However, if we provide the proper credentials:
+
+ >>> request = TestRequest(form={'login':'bob', 'password':'secret'})
+ >>> bob = sau.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 MemberContainer and change the login on the IMember
+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 = sau.authenticate(request)
+ >>> bob2
+ <AuthenticatedPrincipal ...>
+
+ >>> bob2.title
+ u'Bob'
+
+But not with the old one:
+
+ >>> request = TestRequest(form={'login':'bob', 'password':'secret'})
+ >>> sau.authenticate(request) == None
+ True
+
+The member 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 = sau.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 = sau.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 IMyMember(IMyEmail, interfaces.IMember):
+ ... """Custom IMember 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 MyMember(Member):
+ ... zope.interface.implements(IMyMember)
+ ... def __init__(self, login, password, title, description, email):
+ ... super(MyMember, self).__init__(login, password, title,
+ ... description)
+ ... self.email = email
+
+Now we have to define the AuthenticatedPrincipal for MyMember:
+
+ >>> 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 MyMember:
+
+ >>> 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 = MyMember(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 = sau.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.authentication/trunk/src/z3c/authentication/simple/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/SETUP.cfg
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/SETUP.cfg 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/SETUP.cfg 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+ z3c.authentication.simple-*.zcml
+</data-files>
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/SETUP.cfg
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/TODO.txt
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/TODO.txt 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/TODO.txt 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,9 @@
+====
+TODO
+====
+
+- Add timestamp to principal id generation
+
+- Add configure for everything
+
+- Add configurator for the new IAuthentication utility
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/TODO.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/__init__.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/__init__.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/__init__.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,16 @@
+###############################################################################
+#
+# Copyright (c) 2006 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$
+"""
\ No newline at end of file
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/authentication.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/authentication.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/authentication.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,174 @@
+###############################################################################
+#
+# Copyright (c) 2006 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.authentication.interfaces import ICredentialsPlugin
+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.authentication.simple import interfaces
+from z3c.authentication.simple import event
+
+
+class SimpleAuthentication(btree.BTreeContainer):
+ """See z3c.authentication.interfaces.ISimpleAuthentication."""
+
+ zope.interface.implements(IAuthentication,
+ interfaces.ISimpleAuthentication, 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, ICredentialsPlugin)
+
+ def authenticate(self, request):
+ authenticatorPlugins = [p for n, p in self.getAuthenticatorPlugins()]
+ for name, credplugin in self.getCredentialsPlugins():
+ credentials = credplugin.extractCredentials(request)
+ for authplugin in authenticatorPlugins:
+ if authplugin is None:
+ continue
+ 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.ISimpleAuthentication)
+
+ 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.authentication/trunk/src/z3c/authentication/simple/authentication.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/__init__.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/__init__.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/__init__.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,16 @@
+##############################################################################
+#
+# Copyright (c) 2006 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$
+"""
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/add.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/add.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/add.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,106 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+"""Add forms
+
+$Id$
+"""
+
+import zope.interface
+import zope.schema
+from zope.formlib import form
+
+from z3c.authentication.simple import interfaces
+from z3c.authentication.simple import group
+from z3c.authentication.simple import member
+
+
+class IContentName(zope.interface.Interface):
+ """Object name."""
+
+ __name__ = zope.schema.TextLine(
+ title=u'Object Name',
+ description=u'Object Name',
+ required=True)
+
+
+class MemberContainerAddForm(form.AddForm):
+ """MemberContainer add form."""
+
+ label = _('Add Member Container.')
+
+ form_fields = form.Fields(IContentName)
+
+ def create(self, data):
+ obj = member.MemberContainer()
+ self.context.contentName = data.get('__name__', u'')
+ return obj
+
+
+class MemberAddForm(form.AddForm):
+ """Member add form."""
+
+ label = _('Add Member.')
+
+ form_fields = form.Fields(interfaces.IMember).select('login', 'password',
+ 'title', 'description', 'passwordManagerName')
+ form_fields += form.Fields(IContentName)
+
+ def create(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 = member.Member(login, password, title, description,
+ passwordManagerName)
+ name = data.get('__name__', u'')
+ self.context.contentName = name
+ return obj
+
+
+
+class GroupContainerAddForm(form.AddForm):
+ """GroupContainer add form."""
+
+ label = _('Add Group Container.')
+
+ form_fields = form.Fields(interfaces.IGroupContainer).select(
+ 'prefix')
+ form_fields += form.Fields(IContentName)
+
+ def create(self, data):
+ obj = group.GroupContainer()
+ obj.prefix = data.get('prefix', u'')
+ self.context.contentName = data.get('__name__', u'')
+ return obj
+
+
+class GroupAddForm(form.AddForm):
+ """Group add form."""
+
+ label = _('Add Group.')
+
+ form_fields = form.Fields(interfaces.IGroup).select('title', 'description')
+ form_fields += form.Fields(IContentName)
+
+ def create(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.context.prefix
+ if not name.startswith(prefix):
+ name = prefix + name
+ self.context.contentName = name
+ return obj
\ No newline at end of file
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/add.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/adding.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/adding.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/adding.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,27 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+"""Adding that redirects to plugins.html.
+
+$Id$
+"""
+
+from zope.app import zapi
+
+import zope.app.container.browser.adding
+
+class Adding(zope.app.container.browser.adding.Adding):
+
+ def nextURL(self):
+ return zapi.absoluteURL(self.context, self.request
+ ) + '/@@contents.html'
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/adding.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/configure.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/configure.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/configure.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,82 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser"
+ i18n_domain="z3c">
+
+ <addform
+ schema="..interfaces.ISimpleAuthentication"
+ label="Add Simple Authentication"
+ content_factory="..authentication.SimpleAuthentication"
+ name="AddSimpleAuthentication.html"
+ permission="zope.ManageServices"
+ />
+
+ <addMenuItem
+ class="..authentication.SimpleAuthentication"
+ view="AddSimpleAuthentication.html"
+ title="Simple Authentication Utility"
+ description="Simple authentication utility"
+ permission="zope.ManageServices"
+ />
+
+ <page
+ for="..interfaces.ISimpleAuthentication"
+ name="addRegistration.html"
+ permission="zope.ManageSite"
+ class=".register.AddAuthenticationRegistration"
+ />
+
+ <editform
+ schema="..interfaces.ISimpleAuthentication"
+ label="Edit Simple Authentication Utility"
+ name="configure.html"
+ fields="credentialsPlugins authenticatorPlugins"
+ menu="zmi_views" title="Configure"
+ permission="zope.ManageServices"
+ />
+
+ <page
+ name="contents.html"
+ for="..interfaces.ISimpleAuthentication"
+ menu="zmi_views" title="Plugins"
+ permission="zope.ManageSite"
+ class="zope.app.container.browser.contents.Contents"
+ attribute="contents"
+ />
+
+ <view
+ name="+"
+ menu="zmi_actions" title="Add"
+ for="..interfaces.ISimpleAuthentication"
+ permission="zope.ManageSite"
+ class=".adding.Adding"
+ >
+ <page
+ name="index.html"
+ attribute="index"
+ />
+ <page
+ name="action.html"
+ attribute="action"
+ />
+ </view>
+
+ <menuItem
+ menu="zmi_views"
+ for="..interfaces.ISimpleAuthentication"
+ title="Contents"
+ action=""
+ filter="python:False"
+ />
+
+ <zope:adapter
+ for="..interfaces.IQuerySchemaSearch
+ zope.publisher.interfaces.browser.IBrowserRequest"
+ provides="zope.app.form.browser.interfaces.ISourceQueryView"
+ factory=".schemasearch.QuerySchemaSearchView"
+ />
+
+ <include file="group.zcml" />
+ <include file="member.zcml" />
+
+</configure>
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/edit.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/edit.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/edit.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# Copyright (c) 2005 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.
+#
+##############################################################################
+"""Adding that redirects to plugins.html.
+
+$Id$
+"""
+
+from zope.formlib import form
+
+from z3c.authentication.simple import interfaces
+
+
+class EditMember(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit Member.')
+
+ form_fields = form.Fields(interfaces.IMember).select('login', 'password',
+ 'title', 'description', 'passwordManagerName')
+
+
+class EditGroup(form.EditForm):
+ """Group edit form."""
+
+ label = _('Edit Group.')
+
+ form_fields = form.Fields(interfaces.IGroup).select('title',
+ 'description', 'principals')
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/edit.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/group.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/group.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/group.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,53 @@
+<configure
+ xmlns="http://namespaces.zope.org/browser"
+ i18n_domain="z3c">
+
+ <!-- Group -->
+ <page
+ for="zope.app.container.interfaces.IAdding"
+ name="AddGroup.html"
+ class=".add.GroupAddForm"
+ permission="zope.ManageServices"
+ />
+
+ <addMenuItem
+ title="Simple Group"
+ description="A simple group"
+ class="..group.Group"
+ permission="zope.ManageServices"
+ view="AddGroup.html"
+ />
+
+ <page
+ name="edit.html"
+ for="..interfaces.IGroup"
+ class=".edit.EditGroup"
+ menu="zmi_views" title="Edit"
+ permission="zope.ManageServices"
+ />
+
+
+ <!-- GroupContainer -->
+ <page
+ for="zope.app.container.interfaces.IAdding"
+ name="AddGroupContainer.html"
+ class=".add.GroupContainerAddForm"
+ permission="zope.ManageServices"
+ />
+
+ <addMenuItem
+ title="Group Container"
+ description="A Group container"
+ class="..group.GroupContainer"
+ permission="zope.ManageServices"
+ view="AddGroupContainer.html"
+ />
+
+ <containerViews
+ for="..interfaces.IGroupContainer"
+ contents="zope.ManageServices"
+ index="zope.ManageServices"
+ add="zope.ManageServices"
+ />
+
+</configure>
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/group.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/member.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/member.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/member.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,61 @@
+<configure
+ xmlns="http://namespaces.zope.org/browser">
+
+ <!-- Member -->
+ <page
+ for="zope.app.container.interfaces.IAdding"
+ name="AddSimpleMember.html"
+ class=".add.MemberAddForm"
+ permission="zope.ManageServices"
+ />
+ <addMenuItem
+ title="Member"
+ class="..member.Member"
+ permission="zope.ManageServices"
+ view="AddSimpleMember.html"
+ />
+
+ <page
+ name="edit.html"
+ for="..interfaces.IMember"
+ class=".edit.EditMember"
+ menu="zmi_views" title="Edit"
+ permission="zope.ManageServices"
+ />
+
+ <containerViews
+ for="..interfaces.IMemberContainer"
+ add="zope.ManageServices"
+ contents="zope.ManageServices"
+ index="zope.ManageServices"
+ />
+
+
+ <!-- MemberContainer -->
+ <page
+ for="zope.app.container.interfaces.IAdding"
+ name="AddSimpleMemberContainer.html"
+ class=".add.MemberContainerAddForm"
+ permission="zope.ManageServices"
+ />
+
+ <addMenuItem
+ title="Member Container"
+ description="A Pluggable Persistent Authentication Plugin"
+ class="..member.MemberContainer"
+ permission="zope.ManageServices"
+ view="AddSimpleMemberContainer.html"
+ />
+
+ <addform
+ schema="..interfaces.IMember"
+ label="Add Member"
+ content_factory="..member.Member"
+ arguments="login password title"
+ keyword_arguments="passwordManagerName description"
+ fields="login passwordManagerName password title description"
+ name="AddMember.html"
+ permission="zope.ManageServices"
+ />
+
+</configure>
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/member.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/register.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/register.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/register.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Improved registration UI for registering pluggable authentication utilities
+
+$Id$
+"""
+
+from zope.app.i18n import ZopeMessageFactory as _
+import zope.app.component.browser.registration
+import zope.app.security.interfaces
+
+class AddAuthenticationRegistration(
+ zope.app.component.browser.registration.AddUtilityRegistration,
+ ):
+ label = _("Register a pluggable authentication utility")
+ name = ''
+ provided = zope.app.security.interfaces.IAuthentication
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/browser/register.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/browser/schemasearch.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/browser/schemasearch.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/browser/schemasearch.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,112 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Search interface for queriables.
+
+$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.authentication/trunk/src/z3c/authentication/simple/browser/schemasearch.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/configure.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/configure.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/configure.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,43 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="z3c">
+
+ <class class=".authentication.SimpleAuthentication">
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ permission="zope.ManageSite"
+ interface=".interfaces.ISimpleAuthentication"
+ set_schema=".interfaces.ISimpleAuthentication"
+ />
+ <require
+ permission="zope.ManageServices"
+ attributes="registrationManager"
+ />
+ </class>
+
+ <adapter
+ for=".interfaces.IQuerySchemaSearch
+ .interfaces.ISimpleAuthentication"
+ provides=".interfaces.IQueriableAuthenticator"
+ factory=".authentication.QuerySchemaSearchAdapter"
+ />
+
+ <utility
+ component=".vocabulary.authenticatorPlugins"
+ name="SimpleAuthenticatorPlugins"
+ />
+
+ <utility
+ component=".vocabulary.credentialsPlugins"
+ name="SimpleCredentialsPlugins"
+ />
+
+ <include file="principal.zcml" />
+ <include file="group.zcml" />
+ <include file="member.zcml" />
+
+ <include package=".browser" />
+
+</configure>
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/event.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/event.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/event.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,92 @@
+###############################################################################
+#
+# Copyright (c) 2006 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.authentication.simple 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 AbstractMembersChanged(object):
+
+ def __init__(self, principal_ids, group_id):
+ self.principal_ids = principal_ids
+ self.group_id = group_id
+
+ def __repr__(self):
+ return "<%s %r %r>" % (
+ self.__class__.__name__, sorted(self.principal_ids), self.group_id)
+
+
+class PrincipalsAddedToGroup(AbstractMembersChanged):
+ zope.interface.implements(interfaces.IPrincipalsAddedToGroup)
+
+
+class PrincipalsRemovedFromGroup(AbstractMembersChanged):
+ zope.interface.implements(interfaces.IPrincipalsRemovedFromGroup)
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/event.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/group.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/group.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/group.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,313 @@
+###############################################################################
+#
+# Copyright (c) 2006 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.authentication.simple import interfaces
+from z3c.authentication.simple 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'members')
+ >>> 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.members', 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 getMembers(self):
+ return self._group.principals
+
+ def setMembers(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.authentication/trunk/src/z3c/authentication/simple/group.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/group.txt
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/group.txt 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/group.txt 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,582 @@
+=====
+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 members, groups are created when they are needed.
+
+ >>> from z3c.authentication.simple import interfaces
+ >>> from z3c.authentication.simple.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.authentication.simple.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 SimpleAuthentication utility:
+
+ >>> from z3c.authentication.simple.authentication import SimpleAuthentication
+ >>> sau = SimpleAuthentication()
+
+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 zope.app.authentication.interfaces import ICredentialsPlugin
+
+ >>> class MyCredentialsPlugin(object):
+ ...
+ ... zope.interface.implements(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 SimpleAuthentication:
+
+ >>> myCredentialsPlugin = MyCredentialsPlugin()
+ >>> sau['credentials'] = myCredentialsPlugin
+ >>> sau.credentialsPlugins = ('credentials', )
+
+We also need a principal and a IAuthenticationPlugin:
+
+ >>> from z3c.authentication.simple.member import Member
+ >>> p1 = Member(u'p1', u'password', u'Principal 1')
+ >>> p2 = Member(u'p2', u'password', u'Principal 2')
+ >>> p3 = Member(u'p3', u'password', u'Principal 3')
+ >>> p4 = Member(u'p4', u'password', u'Principal 4')
+
+ >>> from z3c.authentication.simple.member import MemberContainer
+ >>> members = MemberContainer()
+ >>> token1, p1 = members.add(p1)
+ >>> token2, p2 = members.add(p2)
+ >>> token3, p3 = members.add(p3)
+ >>> token4, p4 = members.add(p4)
+
+Add the GroupContainer and MemberContainer to the SimpleAuthentication and
+set the correct plugin names
+
+ >>> sau['members'] = members
+ >>> sau['groups'] = groups
+ >>> sau.authenticatorPlugins = ('members', 'groups')
+
+
+Adding members to groups
+------------------------
+
+Now we can set the members on the group but first we need to register the
+IFoundPrincipal adapter for groups. The GroupPrincipal adapter provides this
+interface:
+
+ >>> from z3c.authentication.simple.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.authentication.simple.principal import AuthenticatedPrincipal
+ >>> from z3c.authentication.simple.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.authentication.simple.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 members fires an event.
+
+ >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
+ <PrincipalsAddedToGroup [..., ...] u'groups.g1'>
+
+We can now look up groups for the members:
+
+ >>> 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
+members:
+
+ >>> 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 members 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
+
+ >>> from zope.security.interfaces import IGroup
+ >>> 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
+members 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.authentication.simple.principal import FoundPrincipal
+ >>> from z3c.authentication.simple.event import FoundPrincipalCreated
+ >>> from z3c.authentication.simple.group import specialGroups
+ >>> x = Member('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
+ []
+
+
+MemberAwareGroup
+----------------
+
+We can manages groups via the group getMember and setMember methods:
+
+ >>> foundGroup = interfaces.IFoundPrincipal(g1)
+ >>> foundGroup.getMembers()
+ (..., ...)
+
+ >>> foundGroup.getMembers()[0] == p1.__name__
+ True
+
+ >>> foundGroup.getMembers()[1] == p2.__name__
+ True
+
+Let's remove a member:
+
+ >>> foundGroup.setMembers((p2.__name__,))
+ >>> foundGroup.getMembers()
+ (...,)
+
+ >>> foundGroup.getMembers()[0] == p2.__name__
+ True
+
+ >>> foundGroup.members
+ (...,)
+
+ >>> foundGroup.getMembers()[0] == p2.__name__
+ True
+
+Ensure that such a member provides the IMemberAwareGroup interfaces.
+
+ >>> zope.security.interfaces.IMemberAwareGroup.providedBy(foundGroup)
+ True
+
+#Member-aware groups
+#-------------------
+#The groupfolder includes a subscriber that gives group principals the
+#zope.security.interfaces.IGroupAware interface and an implementation thereof.
+#This allows groups to be able to get and set their members.
+#
+#Given an info object and a 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.setMemberSubscriber(
+# ... interfaces.FoundPrincipalCreated(
+# ... 'dummy auth (ignored)', principal, info))
+# >>> principal.getMembers()
+# ('joe', 'jane')
+# >>> principal.setMembers(('joe', 'jane', 'jaimie'))
+# >>> principal.getMembers()
+# ('joe', 'jane', 'jaimie')
+# >>> zope.security.interfaces.IMemberAwareGroup.providedBy(principal)
+# True
+#
+#The two methods work with the value on the IGroupInformation object.
+#
+# >>> i.principals == principal.getMembers()
+# True
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/group.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/group.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/group.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/group.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -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.authentication/trunk/src/z3c/authentication/simple/group.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/interfaces.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/interfaces.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/interfaces.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,362 @@
+###############################################################################
+#
+# Copyright (c) 2006 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 _
+
+
+# authenticator interfaces
+class IAuthenticatorPlugin(zope.interface.Interface):
+ """Authenticates and provides a principal using credentials."""
+
+ containers('z3c.authentication.simple.interfaces.ISimpleAuthentication')
+
+ 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 ISimpleAuthentication(ILogout, IContainer):
+ """Simple authentication supporting zope 3 credential plugins.
+
+ The SimpleAuthentication supports ICredentialsPlugin plugins defined in
+ zope.app.authentication.interfaces.
+
+ The SimpleAuthentication 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 authentication plugins because we do not
+ use the IPrincipalInfo implementation in this authentication module.
+ """
+
+ contains(IAuthenticatorPlugin, ICredentialsPlugin)
+
+ credentialsPlugins = zope.schema.List(
+ title=_('Credentials Plugins'),
+ description=_("""Used for extracting credentials.
+ Names may be of ids of non-utility ICredentialsPlugins contained in
+ the ISimpleAuthentication, or names of registered
+ ICredentialsPlugins utilities. Contained non-utility ids mask
+ utility names."""),
+ value_type=zope.schema.Choice(vocabulary='SimpleCredentialsPlugins'),
+ 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 ISimpleAuthentication, or names of registered
+ IAuthenticatorPlugins utilities. Contained non-utility ids mask
+ utility names."""),
+ value_type=zope.schema.Choice(vocabulary='SimpleAuthenticatorPlugins'),
+ 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."""
+
+
+
+# member interfaces
+class IMember(zope.interface.Interface):
+ """User"""
+
+ containers('z3c.authentication.simple.interfaces.IMemberContainer')
+
+ login = zope.schema.TextLine(
+ title=_("Login"),
+ description=_("The Login/Username of the principal. "
+ "This value can change."))
+
+ def setPassword(password, passwordManagerName=None):
+ pass
+
+ 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 IMemberSearchCriteria(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 IMemberContainer(IContainer, IAuthenticatorPlugin):
+ """Principal container."""
+
+ contains(IMember)
+
+
+
+# principal interfaces
+class IFoundPrincipal(IGroupClosureAwarePrincipal):
+ """A factory adapting IMember and offering read access to the principal.
+
+ A found principal gets created by the ISimpleAuthentications search method
+ for members 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 member 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 ISimpleAuthentications
+ 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 member 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.authentication.simple.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')
+
+
+# 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.authentication/trunk/src/z3c/authentication/simple/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/member.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/member.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/member.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,255 @@
+###############################################################################
+#
+# Copyright (c) 2006 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.authentication.simple 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 md5.md5(data).hexdigest()
+
+
+class Member(persistent.Persistent, contained.Contained):
+ """User stored in IMemberContainer."""
+
+ zope.interface.implements(interfaces.IMember)
+
+ 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 MemberContainer(btree.BTreeContainer):
+ """A Persistent Member Container and authenticator plugin.
+
+ See principalfolder.txt for details.
+ """
+
+ zope.interface.implements(interfaces.IMemberContainer,
+ interfaces.IQuerySchemaSearch)
+
+ schema = interfaces.IMemberSearchCriteria
+
+ def __init__(self):
+ super(MemberContainer, 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, member):
+ """Add a IPrincipal object within a correct id.
+
+ Create a MemberContainer
+
+ >>> mc = MemberContainer('members.')
+
+ Try to add something not providing IMember
+ >>> try:
+ ... mc.__setitem__(u'max', object())
+ ... except Exception, e:
+ ... print e
+ Item does not support IMember!
+
+ Create a member with no __name__, this should be added via the add
+ method
+
+ >>> member = Member()
+ >>> try:
+ ... mc.__setitem__(u'max', member)
+ ... 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
+
+ >>> member = Member()
+ >>> member.__name__ = u'usertoken'
+ >>> try:
+ ... mc.__setitem__(u'max', member)
+ ... except Exception, e:
+ ... print e
+ Paste a object is not supported!
+
+ Try to use a member with no login:
+
+ >>> try:
+ ... mc.__setitem__(u'max', member)
+ ... except Exception, e:
+ ... print e
+ Member does not provide a login value!
+
+ Add a login attr since __setitem__ is in need of one
+
+ >>> member.login = u'max'
+ >>> mc.__setitem__(u'members.max', principal)
+
+ Now try to use the same login:
+
+ >>> try:
+ ... mc.__setitem__(u'max', member)
+ ... except Exception, e:
+ ... print e
+ Login already taken!
+ """
+
+ # check if we store correct implementations
+ if not interfaces.IMember.providedBy(member):
+ raise TypeError('Item does not support IMember!')
+
+ # check if there is a user id given
+ if member.__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 member.__parent__ is not None:
+ raise ValueError('Paste a object is not supported!')
+
+ # The member doesn't provide a login
+ if not member.login:
+ raise ValueError('Member does not provide a login value!')
+
+ # A user with the new login already exists
+ if member.login in self.__id_by_login:
+ raise DuplicateIDError('Login already taken!')
+
+ super(MemberContainer, self).__setitem__(id, member)
+ self.__id_by_login[member.login] = id
+
+ def add(self, member):
+ token = generateUserIDToken(member.login)
+ # Pre set the user id like a ticket, so we can check it in __setitem__
+ member.__name__ = token
+ self[token] = member
+ return token, self[token]
+
+ def __delitem__(self, id):
+ """Remove principal information."""
+ member = self[id]
+ super(MemberContainer, self).__delitem__(id)
+ del self.__id_by_login[member.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
+ member = self[id]
+ if not member.checkPassword(credentials["password"]):
+ return None
+ return member
+
+ def queryPrincipal(self, id, default=None):
+ member = self.get(id)
+ if member is not None:
+ return member
+ 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.authentication/trunk/src/z3c/authentication/simple/member.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/member.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/member.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/member.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,37 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="z3c">
+
+ <class class=".member.Member">
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IMember"
+ set_schema=".interfaces.IMember"
+ />
+ </class>
+
+ <class class=".member.MemberContainer">
+
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+
+ <require
+ permission="zope.ManageServices"
+ interface="zope.app.container.interfaces.IContainer"
+ />
+
+ <require
+ permission="zope.ManageServices"
+ attributes="prefix"
+ />
+
+ </class>
+
+ <adapter
+ provides="zope.app.container.interfaces.INameChooser"
+ for=".interfaces.IMemberContainer"
+ factory="zope.app.authentication.idpicker.IdPicker"
+ />
+
+</configure>
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/member.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/principal.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/principal.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/principal.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,74 @@
+###############################################################################
+#
+# Copyright (c) 2006 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.authentication.simple 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.IMember)
+
+ zope.interface.implements(interfaces.IAuthenticatedPrincipal)
+
+
+class FoundPrincipal(PrincipalBase):
+ """Default IFoundPrincipal principal."""
+
+ zope.component.adapts(interfaces.IMember)
+
+ zope.interface.implements(interfaces.IFoundPrincipal)
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/principal.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/principal.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/principal.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/principal.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -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.authentication/trunk/src/z3c/authentication/simple/principal.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/tests.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/tests.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/tests.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,51 @@
+###############################################################################
+#
+# Copyright (c) 2006 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 setup
+from zope.app.testing import placelesssetup
+
+
+def siteSetUp(test):
+ site = setup.placefulSetUp(site=True)
+ test.globs['rootFolder'] = site
+
+
+def siteTearDown(test):
+ setup.placefulTearDown()
+
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('README.txt',
+ setUp=siteSetUp, tearDown=siteTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+ doctest.DocFileSuite('group.txt',
+ setUp=siteSetUp, tearDown=siteTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+ doctest.DocTestSuite('z3c.authentication.simple.principal',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown),
+ doctest.DocTestSuite('z3c.authentication.simple.group',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown),
+ doctest.DocFileSuite('vocabulary.txt',
+ setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/tests.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.py
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.py 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.py 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,100 @@
+###############################################################################
+#
+# Copyright (c) 2006 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 zope.app.authentication.interfaces import ICredentialsPlugin
+
+from z3c.i18n import MessageFactory as _
+from z3c.authentication.simple import interfaces
+
+UTILITY_TITLE = _(
+ 'zope.app.authentication.vocabulary-utility-plugin-title',
+ '${name} (a utility)')
+CONTAINED_TITLE = _(
+ 'zope.app.authentication.vocabulary-contained-plugin-title',
+ '${name} (in contents)')
+MISSING_TITLE = _(
+ 'zope.app.authentication.vocabulary-missing-plugin-title',
+ '${name} (not found; deselecting will remove)')
+
+
+def _pluginVocabulary(context, interface, attr_name):
+ """Vocabulary that provides names of plugins of a specified interface.
+
+ Given an interface, the options should include the unique names of all of
+ the plugins that provide the specified interface for the current context--
+ which is expected to be a pluggable authentication utility, hereafter
+ referred to as a SimpleAuthentication).
+
+ These plugins may be objects contained within the SimpleAuthentication
+ ("contained plugins"), or may be utilities registered for the specified
+ interface, found in the context of the SimpleAuthentication
+ ("utility plugins"). Contained plugins mask utility plugins of the same
+ name.
+
+ The vocabulary also includes the current values of the
+ SimpleAuthentication even if they do not correspond to a contained or
+ utility plugin.
+ """
+ terms = {}
+ auth = interfaces.ISimpleAuthentication.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, ICredentialsPlugin, 'credentialsPlugins')
+
+zope.interface.alsoProvides(credentialsPlugins, IVocabularyFactory)
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.txt
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.txt 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/vocabulary.txt 2007-01-18 01:27:07 UTC (rev 72089)
@@ -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 SimpleAuthentication.
+
+These names may be for objects contained within the SimpleAuthentication
+("contained plugins"), or may be utilities registered for the specified
+interface, found in the context of the SimpleAuthentication
+("utility plugins"). Contained plugins mask utility plugins of the same name.
+They also may be names currently selected in the SimpleAuthentication 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 SimpleAuthentication.
+
+ >>> from z3c.authentication.simple 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.ISimpleAuthentication)
+ ... 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.authentication.simple 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.authentication/trunk/src/z3c/authentication/simple/vocabulary.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.authentication/trunk/src/z3c/authentication/simple/z3c.authentication.simple-configure.zcml
===================================================================
--- z3c.authentication/trunk/src/z3c/authentication/simple/z3c.authentication.simple-configure.zcml 2007-01-18 01:09:33 UTC (rev 72088)
+++ z3c.authentication/trunk/src/z3c/authentication/simple/z3c.authentication.simple-configure.zcml 2007-01-18 01:27:07 UTC (rev 72089)
@@ -0,0 +1,6 @@
+<configure
+ xmlns:zcml="http://namespaces.zope.org/zcml">
+
+ <include package="z3c.authentication.simple" />
+
+</configure>
Property changes on: z3c.authentication/trunk/src/z3c/authentication/simple/z3c.authentication.simple-configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list