[Checkins] SVN: z3c.authenticator/trunk/ - Feature: added support for local IUnauthenticatedPrincipal. This is usefull

Roger Ineichen roger at projekt01.ch
Mon Nov 24 18:15:41 EST 2008


Log message for revision 93332:
  - Feature: added support for local IUnauthenticatedPrincipal. This is usefull 
    if you need to apply local roles to IUnauthenticatedPrincipal. This was not 
    possible before and is not possible in zope.app.authentication
  
  - Feature: implemented initial grant view based on ISource widget. Note, this
    source widget terms implementation which is very complex to understand will
    get moved to z3c.from if we fixed the ITerm dependency. Which means ITerm 
    needs to get moved out of zope.app.form first.
  
  - Feature: added support for next utility lookup in authenticate call. By 
    default the principals from the global principalregistry get involved now. 
    You can disable this feature by setting includeNextUtilityForAuthenticate to
    False.
  
  - Feature: added PrincipalRegistryAuthenticatorPlugin which allows to  
    authenticate principals defined in global principal registry.

Changed:
  U   z3c.authenticator/trunk/CHANGES.txt
  U   z3c.authenticator/trunk/setup.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/README.txt
  U   z3c.authenticator/trunk/src/z3c/authenticator/authentication.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml
  U   z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml
  U   z3c.authenticator/trunk/src/z3c/authenticator/event.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/group.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/group.txt
  U   z3c.authenticator/trunk/src/z3c/authenticator/group.zcml
  U   z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/principal.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml
  A   z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.py
  A   z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.zcml
  U   z3c.authenticator/trunk/src/z3c/authenticator/widget.py
  U   z3c.authenticator/trunk/src/z3c/authenticator/widget.zcml

-=-
Modified: z3c.authenticator/trunk/CHANGES.txt
===================================================================
--- z3c.authenticator/trunk/CHANGES.txt	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/CHANGES.txt	2008-11-24 23:15:41 UTC (rev 93332)
@@ -3,15 +3,46 @@
 =======
 
 
-Version 0.5.2dev (unreleased)
+Version 0.6.0dev (unreleased)
 -----------------------------
 
-- feature: implemented z3c.form prefix support in SessionCredentialsPlugin. Now 
+- Feature: added support for local IUnauthenticatedPrincipal. This is usefull 
+  if you need to apply local roles to IUnauthenticatedPrincipal. This was not 
+  possible before and is not possible in zope.app.authentication
+
+- Feature: implemented initial grant view based on ISource widget. Note, this
+  source widget terms implementation which is very complex to understand will
+  get moved to z3c.from if we fixed the ITerm dependency. Which means ITerm 
+  needs to get moved out of zope.app.form first.
+
+- Feature: added support for next utility lookup in authenticate call. By 
+  default the principals from the global principalregistry get involved now. 
+  You can disable this feature by setting includeNextUtilityForAuthenticate to
+  False.
+
+- Feature: added PrincipalRegistryAuthenticatorPlugin which allows to  
+  authenticate principals defined in global principal registry.
+
+- Feature: implemented z3c.form prefix support in SessionCredentialsPlugin. Now 
   there is an option called prefixes which can be used for define a list of 
   used z3c.form prefixes. This makes it simpler for supporting different forms 
   and adjust the credential extraction.
 
+- Renamed IGroupPrincipal to IFoundGroup which makes it more understandable
+  why this adapter implementation is needed. The IFoundGroup adapter is now 
+  also used for zope.security.interfaces.IGroup principals. This makes it 
+  possible to use them in the new principalregistry credential. Provide  
+  deprecation message for the old IGroupPrincipal implementation.
 
+- Removed dependency for zapi. But it's not really gone since other packages
+  use zapi too.
+
+- Removed unused InvalidPrincipalIds and InvalidGroupId exceptions
+
+- Removed unused IMemberAwareGroup support. This interface is not used in zope
+  at all.
+
+
 Version 0.5.1 (2008-04-16)
 --------------------------
 

Modified: z3c.authenticator/trunk/setup.py
===================================================================
--- z3c.authenticator/trunk/setup.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/setup.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -23,7 +23,7 @@
 
 setup (
     name='z3c.authenticator',
-    version='0.5.2dev',
+    version='0.6.0dev',
     author = "Roger Ineichen and the Zope Community",
     author_email = "zope3-dev at zope.org",
     description = "IAuthentication implementation for for Zope3",
@@ -56,7 +56,6 @@
             'zope.publisher',
             'zope.session',
             'zope.testing',
-            'zope.testbrowser',
             ],
         ),
     install_requires = [
@@ -66,11 +65,12 @@
         'z3c.formui',
         'z3c.i18n',
         'z3c.template',
-        'z3c.contents',
         'zope.app.component',
+        'zope.app.container',
         'zope.app.generations',
         'zope.app.security',
         'zope.component',
+        'zope.deprecation',
         'zope.dublincore',
         'zope.event',
         'zope.i18n',
@@ -82,7 +82,6 @@
         'zope.security',
         'zope.session',
         'zope.traversing',
-
         ],
     zip_safe = False,
 )

Modified: z3c.authenticator/trunk/src/z3c/authenticator/README.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/README.txt	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/README.txt	2008-11-24 23:15:41 UTC (rev 93332)
@@ -81,6 +81,37 @@
 data, it should also add corresponding interface declarations.
 
 
+FAQ
+---
+
+Here some usefull hints.
+
+How should I set permission for principals -- You can apply a roles to groups
+  and apply permissions to roles. Or you can directly apply local permisssions 
+  to groups or to principals. After setup this mappings you can grant roles to 
+  groups. I always recommend a principal - group and permission - role mapping, 
+  then this gives you the most possible abstraction which is usefullif it comes 
+  to manage permission and principals without to invoke directly principals and 
+  permissions itself. but of corse you can grant permissions to groups or the
+  worst thing directly to principals. Grant permission to principals is only 
+  useful if it comes to selective local permission settings for selected 
+  principals e.g. a ownership like permission setup.
+
+How can I set permission for all principals -- You can register one
+  group as IEveryone utility. This IGroup utility get applied to all principals.
+
+Can I apply local groups to unauthenticated principals -- Yes this will work.
+  Since the last refactoring I refactored the IGroup implementation which makes
+  it compatible with the principalregistry API. This means you can now register
+  one local group as a unnamed IUnauthenticatedGroup. You can also register one 
+  local group as an unnamed IAuthenticatedGroup utility which will get applied 
+  to every authenticated principal or a unnamed utility for 
+  IUnauthenticatedGroup.
+
+Can I apply a local group to every principal -- Yes, this is possible if 
+  register a local unnamed utility providing IEveryoneGroup.
+
+
 Principal
 ---------
 

Modified: z3c.authenticator/trunk/src/z3c/authenticator/authentication.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/authentication.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/authentication.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -19,12 +19,15 @@
 import zope.interface
 import zope.component
 import zope.event
+from zope.schema.fieldproperty import FieldProperty
 from zope.schema.interfaces import ISourceQueriables
+from zope.location.interfaces import ILocation
 
 from zope.app.component import queryNextUtility
 from zope.app.container import btree
 from zope.app.security.interfaces import IAuthentication
 from zope.app.security.interfaces import PrincipalLookupError
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
 
 from z3c.authenticator import interfaces
 from z3c.authenticator import event
@@ -39,6 +42,9 @@
     authenticatorPlugins = ()
     credentialsPlugins = ()
 
+    includeNextUtilityForAuthenticate = FieldProperty(
+        interfaces.IAuthenticator['includeNextUtilityForAuthenticate'])
+
     def _plugins(self, names, interface):
         for name in names:
             plugin = self.get(name)
@@ -63,6 +69,7 @@
             if credentials is None:
                 # do not invoke the auth plugin without credentials
                 continue
+
             for authplugin in authenticatorPlugins:
                 if authplugin is None:
                     continue
@@ -78,6 +85,13 @@
                     self, authenticated, request))
                 return authenticated
 
+        if self.includeNextUtilityForAuthenticate:
+            next = queryNextUtility(self, IAuthentication, None)
+            if next is not None:
+                principal = next.authenticate(request)
+                if principal is not None:
+                    return principal
+
         return None
 
     def getPrincipal(self, id):
@@ -106,8 +120,33 @@
                 yield name, queriable
 
     def unauthenticatedPrincipal(self):
-        return None
+        """Return unauthenticated principal or None.
+        
+        This allows you to return an unauthenticated principal. This could be
+        usefull if you don't like to fallback to the global unauthenticated
+        principal usage. Why is this usefull. The reason is, if a global
+        principal get returned, there is no event notification involved like
+        we have in IPrincipalCreated which whould allow to apply groups. And
+        there is no way to apply local groups to global unauthenticated
+        principals it they get returned by the global IAuthentication or the
+        fallback implementation. See zope.app.security.principalregistry
+        
+        Usage:
 
+        Return an unauthenticated principal within this method if you need to
+        apply local groups. This allows to apply local groups for the returned 
+        unauthenticated principal if you use a custom subscriber for 
+        IPrincipalCreated. Note, the local group must define the global 
+        unauthenticated principals id in the principals list. Use the zcml
+        directive called unauthenticatedPrincipal for define the global 
+        unauthenticated principal.
+        """
+        principal = zope.component.queryUtility(IUnauthenticatedPrincipal)
+        if principal is not None:
+            zope.event.notify(event.UnauthenticatedPrincipalCreated(self,
+                principal))
+        return principal
+
     def unauthorized(self, id, request):
         challengeProtocol = None
 
@@ -143,4 +182,28 @@
                 next.logout(request)
 
 
+class QueriableAuthenticator(object):
+    """Performs schema-based principal searches adapting ISearchable and
+    IAuthenticator.
 
+    Delegates the search to the adapted authenticator which also provides
+    ISearchable. See IAuthenticator.getQueriables for more infos.
+    """
+    zope.component.adapts(interfaces.ISearchable, interfaces.IAuthenticator)
+
+    zope.interface.implements(interfaces.IQueriableAuthenticator, ILocation)
+
+    def __init__(self, authplugin, pau):
+        # locate them
+        if ILocation.providedBy(authplugin):
+            self.__parent__ = authplugin.__parent__
+            self.__name__ = authplugin.__name__
+        else:
+            self.__parent__ = pau
+            self.__name__ = ""
+        self.authplugin = authplugin
+        self.pau = pau
+
+    def search(self, query, start=None, batch_size=None):
+        for id in self.authplugin.search(query, start, batch_size):
+            yield id

Modified: z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/authenticator.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -70,5 +70,6 @@
 
     label = _('Edit Authenticator.')
 
-    fields = field.Fields(interfaces.IAuthenticator).select('credentialsPlugins',
+    fields = field.Fields(interfaces.IAuthenticator).select(
+        'includeNextUtilityForAuthenticate', 'credentialsPlugins',
         'authenticatorPlugins')

Modified: z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/browser/credential.zcml	2008-11-24 23:15:41 UTC (rev 93332)
@@ -18,7 +18,6 @@
       />
 
 
-
   <!-- register a loginForm.html page for your layer
   <page
       name="loginForm.html" 

Modified: z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/configure.zcml	2008-11-24 23:15:41 UTC (rev 93332)
@@ -29,7 +29,15 @@
       name="Z3CCredentialsPlugins"
       />
 
+  <adapter
+      for=".interfaces.ISearchable
+           .interfaces.IAuthenticator"
+      factory=".authentication.QueriableAuthenticator"
+      provides=".interfaces.IQueriableAuthenticator"
+      />
+
   <include file="credential.zcml" />
+  <include file="principalregistry.zcml" />
   <include file="group.zcml" />
   <include file="password.zcml" />
   <include file="principal.zcml" />

Modified: z3c.authenticator/trunk/src/z3c/authenticator/event.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/event.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/event.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -39,6 +39,22 @@
         self.request = request
 
 
+class UnauthenticatedPrincipalCreated(object):
+    """
+    >>> from zope.interface.verify import verifyObject
+    >>> event = UnauthenticatedPrincipalCreated('authentication', 'principal',
+    ...     'request')
+    >>> verifyObject(interfaces.IUnauthenticatedPrincipalCreated, event)
+    True
+    """
+
+    zope.interface.implements(interfaces.IUnauthenticatedPrincipalCreated)
+
+    def __init__(self, authentication, principal):
+        self.authentication = authentication
+        self.principal = principal
+
+
 class FoundPrincipalCreated(object):
     """
     >>> from zope.interface.verify import verifyObject

Modified: z3c.authenticator/trunk/src/z3c/authenticator/group.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/group.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/group.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -22,22 +22,30 @@
 import zope.interface
 import zope.component
 import zope.event
+import zope.deprecation
+import zope.deferredimport
+
 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 zope.app.security.interfaces import IUnauthenticatedGroup
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
 
 from z3c.authenticator import interfaces
 from z3c.authenticator import event
 
+zope.deferredimport.deprecated(
+    "FoundPrincipal has moved to z3c.authenticator.group.FoundGroup",
+    FoundPrincipal = 'z3c.authenticator.principal:FoundGroup',
+    )
 
+
 class Group(persistent.Persistent, contained.Contained):
     """An implementation of IGroup used by the group container."""
 
@@ -49,6 +57,11 @@
         self.title = title
         self.description = description
 
+    @property
+    def id(self):
+        """The id is the name which includes the container prefix (readonly)."""
+        return self.__name__
+
     def setPrincipals(self, prinlist, check=True):
         # method is not a part of the interface
         parent = self.__parent__
@@ -72,7 +85,8 @@
 
         if check:
             try:
-                nocycles(new, [], zapi.principals().getPrincipal)
+                auth = zope.component.getUtility(IAuthentication)
+                nocycles(new, [], auth.getPrincipal)
             except GroupCycle:
                 # abort
                 self.setPrincipals(old, False)
@@ -204,107 +218,76 @@
         return self.get(id, default)
 
 
-class GroupPrincipal(object):
-
-    zope.interface.implements(interfaces.IGroupPrincipal)
-    zope.component.adapts(interfaces.IGroup)
-
-    def __init__(self, group):
-        self.id = group.__name__
-        self._group = group
-        self.groups = []
-
-    @property
-    def allGroups(self):
-        if self.groups:
-            seen = set()
-            auth = zope.component.getUtility(IAuthentication)
-            stack = [iter(self.groups)]
-            while stack:
-                try:
-                    group_id = stack[-1].next()
-                except StopIteration:
-                    stack.pop()
-                else:
-                    if group_id not in seen:
-                        yield group_id
-                        seen.add(group_id)
-                        group = auth.getPrincipal(group_id)
-                        stack.append(iter(group.groups))
-
-    @property
-    def title(self):
-        return self._group.title
-
-    @property
-    def description(self):
-        return self._group.description
-
-    @apply
-    def members():
-        def get(self):
-            return self._group.principals
-        def set(self, value):
-            self._group.principals = value
-        return property(get, set)
-
-    def getUsers(self):
-        return self._group.principals
-
-    def setUsers(self, value):
-        self._group.principals = value
-
-    def __repr__(self):
-        return "<%s %s>" %(self.__class__.__name__, self.id)
-
-
 class GroupCycle(Exception):
     """There is a cyclic relationship among groups."""
 
-class InvalidPrincipalIds(Exception):
-    """A user has a group id for a group that can't be found
-    """
 
-class InvalidGroupId(Exception):
-    """A user has a group id for a group that can't be found
-    """
-
 def nocycles(pids, seen, getPrincipal):
     for pid in pids:
         if pid in seen:
             raise GroupCycle(pid, seen)
         seen.append(pid)
         principal = getPrincipal(pid)
-        nocycles(principal.groups, seen, getPrincipal)
+        # not every principal has groups and there is no consistent marker
+        # interface for mark group aware. See IUnauthenticatedPrincipal
+        groups = getattr(principal, 'groups', ())
+        nocycles(groups, seen, getPrincipal)
         seen.pop()
 
 
+# specialGroups
+ at zope.component.adapter(interfaces.IPrincipalCreated)
 def specialGroups(event):
+    """Set groups for IGroupAwarePrincipal."""
+
     principal = event.principal
+    # only apply to non groups because it will end in cycle dependencies
+    # since the principal will have tis role anyway
     if (IGroup.providedBy(principal) or
-        not IGroupAwarePrincipal.providedBy(principal)):
+        not (IGroupAwarePrincipal.providedBy(principal) or
+             IUnauthenticatedPrincipal.providedBy(principal))):
         return
 
+    # global utility registered by everybodyGroup directive
     everyone = zope.component.queryUtility(IEveryoneGroup)
-    if everyone is not None:
+    if everyone is not None and everyone.id != principal.id:
         principal.groups.append(everyone.id)
 
-    auth = zope.component.queryUtility(IAuthenticatedGroup)
-    if auth is not None:
-        principal.groups.append(auth.id)
+    if IUnauthenticatedPrincipal.providedBy(principal):
+        # global utility registered by unauthenticatedGroup directive
+        unAuthGroup = zope.component.queryUtility(IUnauthenticatedGroup)
+        if unAuthGroup is not None and unAuthGroup.id != principal.id:
+            principal.groups.append(unAuthGroup.id)
+    else:
+        # global utility registered by authenticatedGroup directive
+        authGroup = zope.component.queryUtility(IAuthenticatedGroup)
+        if authGroup is not None and authGroup.id != principal.id:
+            principal.groups.append(authGroup.id)
 
 
+ at zope.component.adapter(interfaces.IPrincipalCreated)
 def setGroupsForPrincipal(event):
-    """Set group information when a principal is created"""
+    """Set local group information when a principal is created.
+    
+    Note: IUnauthenticatedPrincipal does not provide IGroupAwarePrincipal which
+    is just wrong and makes the conditions a little bit complicated.
+    """
 
     principal = event.principal
-    if not IGroupAwarePrincipal.providedBy(principal):
+    # set only groups for group aware principals or unauthenticated which are
+    # group aware too. This allows us to apply local roles to unautenticated
+    # principals which allows to apply permissions/roles via local groups which
+    # the application does not provide at global level.
+    if not (IGroupAwarePrincipal.providedBy(principal) or
+            IUnauthenticatedPrincipal.providedBy(principal)):
         return
 
     authentication = event.authentication
-
     for name, plugin in authentication.getAuthenticatorPlugins():
         if not interfaces.IGroupContainer.providedBy(plugin):
             continue
+        # set groups for principals but not a group to itself. This could happen
+        # for global defined groups
         principal.groups.extend(
-            [id for id in plugin.getGroupsForPrincipal(principal.id)])
+            [id for id in plugin.getGroupsForPrincipal(principal.id)
+             if id != principal.id])

Modified: z3c.authenticator/trunk/src/z3c/authenticator/group.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/group.txt	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/group.txt	2008-11-24 23:15:41 UTC (rev 93332)
@@ -140,11 +140,11 @@
 ----------------------
 
 Now we can set the users on the group but first we need to register the
-IFoundPrincipal adapter for groups. The GroupPrincipal adapter provides this
+IFoundPrincipal adapter for groups. The FoundGroup adapter provides this
 interface:
 
-  >>> from z3c.authenticator.group import GroupPrincipal
-  >>> zope.component.provideAdapter(GroupPrincipal, 
+  >>> from z3c.authenticator.principal import FoundGroup
+  >>> zope.component.provideAdapter(FoundGroup, 
   ...     provides=interfaces.IFoundPrincipal)
 
 And we also need to provide the IFoundPrincipal and IAuthenticatedPrincipal
@@ -329,6 +329,7 @@
   >>> g1.principals
   (..., ...)
 
+
   >>> g1.principals[0] == p1.__name__
   True
 
@@ -380,6 +381,7 @@
   >>> list(groups.search({}))
   []
 
+
 Identifying groups
 ------------------
 
@@ -401,10 +403,10 @@
   >>> g1.groups
   [u'groups.G2']
 
-A GroupPrincipal provides IGroupPrincipal which is inherited from 
+A FoundGroup provides IFoundGroup which is inherited from 
 IFoundPrincipal and IGroup:
 
-  >>> interfaces.IGroupPrincipal.providedBy(g1)
+  >>> interfaces.IFoundGroup.providedBy(g1)
   True
 
   >>> interfaces.IFoundPrincipal.providedBy(g1)
@@ -415,14 +417,15 @@
   True
 
 
-Special groups
---------------
+specialGroups
+-------------
 
-Two special groups, Authenticated, and Everyone may apply to users
-created by the pluggable-authentication utility.  There is a
-subscriber, specialGroups, that will set these groups on any non-group
-users if IAuthenticatedGroup, or IEveryoneGroup utilities are
-provided.
+Two special groups, IAuthenticatedGroup, and IEveryoneGroup may apply to users
+created by the IAuthentication utility.  There is a subscriber called 
+``specialGroups``. This subscriber can set this special groups on any 
+principal if IAuthenticatedGroup, or IEveryoneGroup utilities are 
+provided. The subscriber knows also how to apply local groups to principals.
+Note, principals means IAuthenticatedPrincipal, IFoundPrincipal or IFoundGroup.
 
 If we notify the subscriber with the principal, nothing will happen
 because the groups haven't been defined:
@@ -440,14 +443,13 @@
 Now, if we define the Everybody group:
 
   >>> import zope.app.security.interfaces
-  >>> class EverybodyGroup(GroupPrincipal):
+  >>> class EverybodyGroup(Group):
   ...     zope.interface.implements(
   ...        zope.app.security.interfaces.IEveryoneGroup)
 
-  >>> all = Group('groups.all')
+  >>> all = EverybodyGroup('groups.all')
   >>> groups['groups.all'] = all
-  >>> everybody = EverybodyGroup(all)
-  >>> zope.component.provideUtility(everybody,
+  >>> zope.component.provideUtility(all,
   ...     zope.app.security.interfaces.IEveryoneGroup)
 
 Then the group will be added to the principal:
@@ -458,13 +460,12 @@
 
 Similarly for the authenticated group:
 
-  >>> class AuthenticatedGroup(GroupPrincipal):
+  >>> class AuthenticatedGroup(Group):
   ...     zope.interface.implements(
   ...         zope.app.security.interfaces.IAuthenticatedGroup)
 
-  >>> authenticated = Group('groups.authenticated')
+  >>> authenticated = AuthenticatedGroup('groups.authenticated')
   >>> groups['groups.authenticated'] = authenticated
-  >>> authenticated = AuthenticatedGroup(authenticated)
   >>> zope.component.provideUtility(authenticated,
   ...     zope.app.security.interfaces.IAuthenticatedGroup)
 
@@ -476,68 +477,54 @@
   >>> found.groups
   [u'groups.all', u'groups.authenticated']
 
-The `allGroups` attribute is a readonly iterable of the full closure of the
-groups in the `groups` attribute--that is, if the principal is a direct member
-of the 'Administrators' group, and the 'Administrators' group is a member of
-the 'Reviewers' group, then p.groups would be ['Administrators'] and 
-list(p.allGroups) would be ['Administrators', 'Reviewers'].
 
-  >>> sorted(found.allGroups)
-  [u'groups.all', u'groups.authenticated']
+allGroups
+---------
 
+The `allGroups` attribute is a readonly iterable of the full closure of the
+groups in the `groups` attribute. Let's define a new principal first:
 
-These groups are only added to non-group principals:
+  >>> p = User(u'p', u'password', u'Principal')
+  >>> token, p = users.add(p)
 
-  >>> found.groups = []
-  >>> zope.interface.directlyProvides(found, zope.security.interfaces.IGroup)
-  >>> specialGroups(event)
-  >>> found.groups
-  []
+And the groups:
 
-And they are only added to group aware principals:
+  >>> ga = Group("Administrators")
+  >>> gr = Group("Reviewers")
+  >>> gid, ga = groups.addGroup(u'Administrators', ga)
+  >>> gid, gr = groups.addGroup(u'Reviewers', gr)
 
-  >>> class SolitaryPrincipal:
-  ...     zope.interface.implements(zope.security.interfaces.IPrincipal)
-  ...     id = title = description = ''
+If the principal is a direct member of the 'Administrators' group, 
 
-  >>> event = FoundPrincipalCreated(authenticator, SolitaryPrincipal())
-  >>> specialGroups(event)
-  >>> found.groups
-  []
+  >>> ga.principals = [p.__name__]
 
+then getGroupsForPrincipal would be ['Administrators']
 
+  >>> groups.getGroupsForPrincipal(p.__name__)
+  (u'groups.Administrators',)
+  
+and if the 'Administrators' group is a member of the 'Reviewers' group, 
 
-UserAwareGroup
-----------------
+  >>> gr.principals = [ga.id]
 
-We can manages groups via the group getUser and setUser methods:
+then groups would be ['Administrators'] too.
 
-  >>> foundGroup = interfaces.IFoundPrincipal(g1)
-  >>> foundGroup.getUsers()
-  (..., ...)
+  >>> groups.getGroupsForPrincipal(p.__name__)
+  (u'groups.Administrators',)
 
-  >>> foundGroup.getUsers()[0] == p1.__name__
-  True
+now let's use the setGroupsForPrincipal subscriber which knows how to apply
+the groups to the found principal:
 
-  >>> foundGroup.getUsers()[1] == p2.__name__
-  True
+  >>> pFound = FoundPrincipal(p)
+  >>> event = FoundPrincipalCreated(authenticator, pFound)
+  >>> setGroupsForPrincipal(event)
 
-Let's remove a member from the group:
+As you can see and pFound.groups is ['Administrators'].
 
-  >>> foundGroup.setUsers((p2.__name__,))
-  >>> foundGroup.getUsers()
-  (...,)
+  >>> sorted(pFound.groups)
+  [u'groups.Administrators']
 
-  >>> foundGroup.getUsers()[0] == p2.__name__
-  True
+And pFound.allGroups is ['Administrators', 'Reviewers'].
 
-  >>> foundGroup.members
-  (...,)
-
-  >>> foundGroup.getUsers()[0] == p2.__name__
-  True
-
-Ensure that such a group provides the IMemberAwareGroup interfaces.
-
-  >>> zope.security.interfaces.IMemberAwareGroup.providedBy(foundGroup)
-  True
+  >>> sorted(pFound.allGroups)
+  [u'groups.Administrators', u'groups.Reviewers']

Modified: z3c.authenticator/trunk/src/z3c/authenticator/group.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/group.zcml	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/group.zcml	2008-11-24 23:15:41 UTC (rev 93332)
@@ -22,15 +22,10 @@
         />
     <require
         permission="zope.ManageServices"
-        interface=".interfaces.IGroupContainer
-                   zope.app.container.interfaces.INameChooser"
+        interface=".interfaces.IGroupContainer"
         />
   </class>
 
-  <adapter
-      factory=".group.GroupPrincipal"
-      />
-
   <subscriber
       for=".interfaces.IPrincipalCreated"
       handler=".group.specialGroups"

Modified: z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -18,6 +18,8 @@
 
 import zope.interface
 import zope.schema
+import zope.deprecation
+import zope.security.interfaces
 
 from zope.security.interfaces import IGroupClosureAwarePrincipal
 from zope.security.interfaces import IMemberAwareGroup
@@ -30,7 +32,7 @@
 from z3c.i18n import MessageFactory as _
 
 
-# TODO: this shoulld really, really go to another place then 
+# TODO: this should really, really go to another place then
 #       zope.app.authentication
 class IPasswordManager(zope.interface.Interface):
     """Password manager."""
@@ -66,7 +68,29 @@
         If the plugin cannot find information for the id, returns None.
         """
 
+class IPrincipalRegistryAuthenticatorPlugin(IAuthenticatorPlugin):
+    """Principal registry authenticator plugin.
+    
+    This plugin is a little bit special since principals get returned from
+    a IAuthentication source next to the root then any other IAuthenticator.
+    
+    By defaut this utility returns global principals and the IAuthenticator
+    forces to wrap then within a IFoundPrincipal. This allows us to apply
+    local groups to gloal defined principals. 
+    
+    You can trun of this feature by set allowQueryPrincipal to False.
+    Anyway, this is just an optional plugin, you don't have to use it.
+    """
 
+    allowQueryPrincipal = zope.schema.Bool(
+        title=_('Allow query principal'),
+        description=_('Allow query principal. This is usefull if an '
+                      'authenticator plugin manages principals for another '
+                      'authenticator.'),
+        default=True,
+        )
+
+
 class ICredentialsPlugin(IPlugin):
     """Handles credentials extraction and challenges per request."""
 
@@ -127,7 +151,7 @@
     """Authentication utility.
     
     The Authenticator supports NOT IAuthenticatorPlugin plugins defined 
-    in z3c.authenticator.interfaces. Because they use and return a 
+    in zope.app.authentication.interfaces. Because they use and return a 
     IPrincipalInfo object in the authenticateCredentials method.
     
     Note: you have to write your own authenticator plugins because we do not 
@@ -136,6 +160,12 @@
 
     contains(IPlugin)
 
+    includeNextUtilityForAuthenticate = zope.schema.Bool(
+        title=_('Include next utility for authenticate'),
+        description=_('Include next utility for authenticate'),
+        default=True,
+        )
+
     credentialsPlugins = zope.schema.List(
         title=_('Credentials Plugins'),
         description=_("""Used for extracting credentials.
@@ -198,8 +228,8 @@
         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
+        # TODO: The password manager name may get changed if the password get
+        #       changed!
         readonly=True
         )
 
@@ -236,10 +266,17 @@
 
 
 # principal interfaces
-class IFoundPrincipal(IGroupClosureAwarePrincipal):
-    """A factory adapting IUser and offering read access to the principal.
+class IFoundPrincipal(zope.security.interfaces.IGroupClosureAwarePrincipal):
+    """Provides found principal returned by IAuthenticator.getPrincipal.
     
-    A found principal gets created by the IAuthenticators search method
+    The only goal of this adapter is to provide a none persistent object which
+    we can apply our group of group ids at runtime.
+    
+    This is needed because there is no way to keep the group of group info
+    in sync if we whould store them in a IGroup implementation as persistent 
+    information.
+    
+    A found principal gets also created by the IAuthenticators search method
     for users matching the search critaria.
     """
 
@@ -264,7 +301,8 @@
         required=False)
 
 
-class IAuthenticatedPrincipal(IGroupClosureAwarePrincipal):
+class IAuthenticatedPrincipal(
+    zope.security.interfaces.IGroupClosureAwarePrincipal):
     """A factory adapting IInternalPrincipal and offering read access to the 
     principal.
     
@@ -295,10 +333,29 @@
 
 
 # group interfaces
-class IGroup(zope.interface.Interface):
+class IGroup(zope.security.interfaces.IGroup):
+    """IGroup provides the zope.security.interfaces.IGroup.
+    
+    This let us use such IGroups as local registered IEveryoneGroup or 
+    IAuthenticatedGroup utilities.
+    
+    Note zope.security.interfaces.IGroup implementations are used for store
+    IPrincipal ids of other IPrincipal or IGroup objects.
+    
+    zope.security.interfaces.IGroup implemenations are not used for store
+    group of group informations. Group of gorup information are collected
+    at runtime by the IAuthentication.getPrincipal method via an adapter
+    called IFoundPrincipal. I really hope this is understandable.
+    """
 
     containers('z3c.authenticato.interfaces.IGroupContainer')
 
+    id = zope.schema.TextLine(
+        title=_("Id"),
+        description=_("The unique identification of the principal."),
+        required=True,
+        readonly=True)
+
     title = zope.schema.TextLine(
         title=_("Title"),
         description=_("Provides a title for the permission."),
@@ -338,12 +395,25 @@
         """Get principals which belong to the group"""
 
 
-class IGroupPrincipal(IFoundPrincipal, IMemberAwareGroup):
-    """IGroup that acts as a principal representing a group."""
+class IFoundGroup(IFoundPrincipal, zope.security.interfaces.IGroup):
+    """IFoundGroup acts as a IFoundPrincipal representing a group.
+    
+    This group principal is used as IFoundPrincipal adapter which we adhoc
+    apply our inherited groups incouding groups of groups. See IFoundPrincipal
+    for more information.
+    
+    This means both interface z3c.authenticator.interfaces.IGroupPrincipal and 
+    z3c.authenticator.interfaces.IGroup provide zope.security.interfaces.IGroup.
+    """
 
-    members = zope.interface.Attribute('an iterable of members of the group')
+#    members = zope.interface.Attribute('an iterable of members of the group')
 
+# TODO: deprecate and remove later
+IGroupPrincipal = IFoundGroup
+zope.deprecation.deprecated('IGroupPrincipal',
+    'The IGroupPrincipal interface get replaced by IFoundGroup')
 
+
 # principal event interfaces
 class IPrincipalCreated(zope.interface.Interface):
     """A principal has been created."""
@@ -361,6 +431,10 @@
         "The request the user was authenticated against")
 
 
+class IUnauthenticatedPrincipalCreated(IPrincipalCreated):
+    """A unauthenticated principal has been created."""
+
+
 class IFoundPrincipalCreated(IPrincipalCreated):
     """A principal has been created by way of a search operation."""
 

Modified: z3c.authenticator/trunk/src/z3c/authenticator/principal.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/principal.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/principal.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -18,6 +18,7 @@
 
 import zope.interface
 import zope.component
+from zope.security.interfaces import IPrincipal
 from zope.app.security.interfaces import IAuthentication
 
 from z3c.authenticator import interfaces
@@ -38,6 +39,7 @@
 
     @property
     def allGroups(self):
+        """This method is not used in zope by default, but nice to have it."""
         if self.groups:
             seen = set()
             auth = zope.component.getUtility(IAuthentication)
@@ -72,3 +74,59 @@
     zope.component.adapts(interfaces.IUser)
 
     zope.interface.implements(interfaces.IFoundPrincipal)
+
+
+class AuthenticatedPrincipalForPrincipal(PrincipalBase):
+    """IAuthenticatedPrincipal principal for IPrincipal."""
+
+    zope.component.adapts(IPrincipal)
+
+    zope.interface.implements(interfaces.IAuthenticatedPrincipal)
+
+
+class FoundPrincipalForPrincipal(PrincipalBase):
+    """IFoundPrincipal principal for IPrincipal."""
+
+    zope.component.adapts(IPrincipal)
+
+    zope.interface.implements(interfaces.IFoundPrincipal)
+
+
+class FoundGroup(object):
+
+    zope.interface.implements(interfaces.IFoundGroup)
+    zope.component.adapts(zope.security.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
+
+    def __repr__(self):
+        return "<%s %s>" %(self.__class__.__name__, self.id)

Modified: z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/principal.zcml	2008-11-24 23:15:41 UTC (rev 93332)
@@ -2,8 +2,15 @@
     xmlns="http://namespaces.zope.org/zope"
     i18n_domain="z3c">
 
+  <!-- z3c.authenticator IUser adapters -->
   <adapter factory=".principal.FoundPrincipal" />
-
   <adapter factory=".principal.AuthenticatedPrincipal" />
 
+  <!-- zope.security IPrincipal adapters -->
+  <adapter factory=".principal.FoundPrincipalForPrincipal" />
+  <adapter factory=".principal.AuthenticatedPrincipalForPrincipal" />
+
+  <!-- IGrup adapters -->
+  <adapter factory=".principal.FoundGroup" />
+
 </configure>

Added: z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.py	                        (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -0,0 +1,69 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id:$
+"""
+__docformat__ = "reStructuredText"
+
+import persistent
+import zope.interface
+import zope.security.management
+import zope.security.interfaces
+from zope.app.container import contained
+from zope.app.security.interfaces import PrincipalLookupError
+from zope.app.security.principalregistry import principalRegistry
+
+from z3c.authenticator import interfaces
+
+
+class PrincipalRegistryAuthenticatorPlugin(persistent.Persistent,
+    contained.Contained):
+    """Authenticator Plugin for PrincipalREgistry defined principals.
+    
+    This allows us to authenticate principals defined in principal registry.
+    """
+
+    zope.interface.implements(interfaces.IPrincipalRegistryAuthenticatorPlugin)
+
+    allowQueryPrincipal = FieldProperty(
+        interfaces.IPrincipalRegistryAuthenticatorPlugin['allowQueryPrincipal'])
+
+    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
+
+        # get the principal from the principal registry and validate
+        try:
+            p = principalRegistry.getPrincipalByLogin(credentials['login'])
+            if p.validate(credentials["password"]):
+                return p
+        except KeyError, e:
+            return None
+        except AttributeError, e:
+            return None
+
+    def queryPrincipal(self, id, default=None):
+        # This will force to use a IFoundPrincipal and allows to apply local
+        # groups to global defined principals at local site level. You can
+        # disable this feature by set the allowQueryPrincipal option to False.
+        if self.allowQueryPrincipal:
+            try:
+                return principalRegistry.getPrincipal(id) or default
+            except PrincipalLookupError, e:
+                pass
+        return default

Added: z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.zcml	                        (rev 0)
+++ z3c.authenticator/trunk/src/z3c/authenticator/principalregistry.zcml	2008-11-24 23:15:41 UTC (rev 93332)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="z3c">
+
+  <class class=".principalregistry.PrincipalRegistryAuthenticatorPlugin">
+    <implements
+        interface="zope.annotation.interfaces.IAttributeAnnotatable"
+        />
+    <require
+        permission="zope.ManageServices"
+        interface=".interfaces.IAuthenticatorPlugin"
+        />
+  </class>
+
+</configure>

Modified: z3c.authenticator/trunk/src/z3c/authenticator/widget.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/widget.py	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/widget.py	2008-11-24 23:15:41 UTC (rev 93332)
@@ -24,7 +24,6 @@
 import zope.schema.interfaces
 import zope.app.security.interfaces
 from zope.traversing import api
-from zope.location.interfaces import ILocation
 from zope.app.security.interfaces import IAuthentication
 from zope.app.security.interfaces import IPrincipalSource
 
@@ -170,32 +169,6 @@
         raise NotImplementedError('Source queriable does not provide lenght.')
 
 
-class QueriableAuthenticator(object):
-    """Performs schema-based principal searches on behalf of a PAU.
-
-    Delegates the search to the adapted authenticator (which also provides
-    ISearchable)..
-    """
-    zope.component.adapts(interfaces.ISearchable, interfaces.IAuthenticator)
-
-    zope.interface.implements(interfaces.IQueriableAuthenticator, ILocation)
-
-    def __init__(self, authplugin, pau):
-        # locate them
-        if ILocation.providedBy(authplugin):
-            self.__parent__ = authplugin.__parent__
-            self.__name__ = authplugin.__name__
-        else:
-            self.__parent__ = pau
-            self.__name__ = ""
-        self.authplugin = authplugin
-        self.pau = pau
-
-    def search(self, query, start=None, batch_size=None):
-        for id in self.authplugin.search(query, start, batch_size):
-            yield id
-
-
 class ISearchFormResultsField(zope.schema.interfaces.IList):
     """Search form results field.
 
@@ -334,7 +307,6 @@
         self.widgets['results'].searchResults = value
         self.widgets['results'].update()
 
-# TODO:add condition
     @button.buttonAndHandler(_('Add'), condition=hasResults)
     def handleAdd(self, action):
         """Set search result"""

Modified: z3c.authenticator/trunk/src/z3c/authenticator/widget.zcml
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/widget.zcml	2008-11-24 20:26:10 UTC (rev 93331)
+++ z3c.authenticator/trunk/src/z3c/authenticator/widget.zcml	2008-11-24 23:15:41 UTC (rev 93332)
@@ -4,13 +4,6 @@
     i18n_domain="zope">
 
   <adapter
-      for=".interfaces.ISearchable
-           .interfaces.IAuthenticator"
-      provides=".interfaces.IQueriableAuthenticator"
-      factory=".widget.QueriableAuthenticator"
-      />
-
-  <adapter
       factory=".widget.PrincipalSourceDataConverter"
       />
   <adapter



More information about the Checkins mailing list