[Zope3-checkins] SVN: Zope3/trunk/src/zope/ Moved IPrincipal, IPermission to zope.security.interface

Amos Brocco amos.brocco at mriyasoftware.com
Wed Oct 13 09:30:22 EDT 2004


Log message for revision 28077:
  Moved IPrincipal, IPermission to zope.security.interface
  
  Added the IGroup interface to zope.security.interface
  
  Added groups attribute to IPrincipal
  
  Created IGroupsfolder interface in zope.app.groupscontainer.interfaces that manages groups and groups <-> principals mappings
  
  Added subscriber for IPrincipalCreatedEvent that assigns groups to newly created principals
  
  Added subscriber for IGroupChangedEvent to update principals' group assignments when group information is changed
  
  sprinting with Claudia Albisetti
  
  
  

Changed:
  A   Zope3/trunk/src/zope/app/groupscontainer/
  A   Zope3/trunk/src/zope/app/groupscontainer/__init__.py
  A   Zope3/trunk/src/zope/app/groupscontainer/configure.zcml
  A   Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.py
  A   Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.txt
  A   Zope3/trunk/src/zope/app/groupscontainer/interfaces.py
  A   Zope3/trunk/src/zope/app/groupscontainer/tests.py
  U   Zope3/trunk/src/zope/app/security/configure.zcml
  U   Zope3/trunk/src/zope/app/security/interfaces.py
  U   Zope3/trunk/src/zope/app/security/vocabulary.py
  U   Zope3/trunk/src/zope/security/interfaces.py

-=-
Added: Zope3/trunk/src/zope/app/groupscontainer/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/groupscontainer/__init__.py	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/groupscontainer/__init__.py	2004-10-13 13:30:21 UTC (rev 28077)
@@ -0,0 +1 @@
+# Groupsfolder package 

Added: Zope3/trunk/src/zope/app/groupscontainer/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/groupscontainer/configure.zcml	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/groupscontainer/configure.zcml	2004-10-13 13:30:21 UTC (rev 28077)
@@ -0,0 +1,48 @@
+<configure 
+    xmlns='http://namespaces.zope.org/zope'
+    xmlns:browser='http://namespaces.zope.org/browser'
+    i18n_domain="groupscontainer"
+    xmlns:i18n="http://namespaces.zope.org/i18n"
+    >
+    
+  <browser:tool
+      interface=".interfaces.IGroupsFolder"
+      title="Groups Folder"
+      description="Groups Folder"
+      />
+
+  <localUtility class=".groupsfolder.GroupsFolder">
+    <implements
+        interface=".interfaces.IGroupsFolder" />
+    <implements
+        interface="zope.app.annotation.IAttributeAnnotatable" />
+    <require
+        permission="zope.ManageServices"
+        interface="zope.app.container.interfaces.IContainer" />
+  </localUtility>
+
+  <browser:addMenuItem
+      title="Groups Folder"
+      description="A Groups folder"
+      class=".groupsfolder.GroupsFolder"
+      permission="zope.ManageServices"
+      />
+      
+<browser:containerViews
+     for=".interfaces.IGroupsFolder"
+     contents="zope.ManageContent"
+     index="zope.View"
+     add="zope.ManageContent"
+     />
+      
+</configure> 
+
+
+
+
+
+
+
+
+
+

Added: Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.py
===================================================================
--- Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.py	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.py	2004-10-13 13:30:21 UTC (rev 28077)
@@ -0,0 +1,68 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Zope Groups Folder implementation
+
+$Id: groupsfolder.py 27237 2004-10-12 09:33:00 mriya3 $
+
+"""
+
+from zope.app.groupscontainer.interfaces import IGroupsFolder
+from zope.app.container.btree import BTreeContainer
+from zope.interface import implements
+from BTrees.OOBTree import OOBTree
+
+
+
+class GroupsFolder(BTreeContainer):
+
+    implements(IGroupsFolder)
+    
+    def __init__(self):
+        super(BTreeContainer,self).__init__()
+        # __inversemapping is used to map principals to groups
+        self.__inverseMapping = OOBTree()
+
+    def __delitem__(self, name):
+        """ Removes a group and updates the inverse mapping"""
+        for principal in self.__inverseMapping.keys():
+            groupListForPrincipal = self.__inverseMapping[principal]
+            if name in groupListForPrincipal:
+                groupListForPrincipal.remove(name)
+        super(BTreeContainer,self).__delitem__(name)
+                
+    def __setitem__(self, name, object):
+        """Adds a new group and updates the inverse mapping"""
+        super(BTreeContainer,self).__setitem__(name, object)
+        principalsInGroup = object.principals
+        for principal in principalsInGroup:
+            if self.__inverseMapping.has_key(principal):
+                self.__inverseMapping[principal].append(name)
+            else:
+                self.__inverseMapping[principal] = [name]
+                     
+   
+    def getGroupsForPrincipal(self, principalid):
+        """Get groups the given principal belongs to"""
+        if principalid in self.__inverseMapping.keys():
+            return self.__inverseMapping[principalid]
+        else:
+            return []
+        
+        
+    def getPrincipalsForGroup(self, groupid):
+        """Get principals which belong to the group"""
+        if groupid in self.keys():
+            return self.__getitem__(groupid).principals
+        else:
+            return []

Added: Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.txt
===================================================================
--- Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.txt	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/groupscontainer/groupsfolder.txt	2004-10-13 13:30:21 UTC (rev 28077)
@@ -0,0 +1,75 @@
+Groups Folder Implementation
+========================
+
+The GroupsFolder class offers a groups container that mantains maps between groups and principals as 
+well as maps from principals to groups.
+
+We test that we can add a Group to a GroupsFolder;
+we also check that it is possible to retrieve principals
+from a given group id and, vice-versa, get a list of groups
+a principal belongs to by providing its id.
+   
+      >>> import zope.interface
+      >>> from zope.security.interfaces import IPrincipal, IGroup
+      >>> from zope.security.group import Group
+      >>> from zope.app.groupscontainer.interfaces import IGroupsFolder, IGroupContained
+      >>> from zope.app.groupscontainer.groupsfolder import GroupsFolder
+      >>> from zope.interface import implements
+      >>> class GoldrakePrincipal:
+      ...     implements(IPrincipal)
+      ...     id = '0'
+      ...     title = 'Goldrake'
+      ...     description = 'Ufo robot'
+      ...     groups = ['superheroes', 'robots']
+      >>> class MazingaPrincipal:
+      ...     implements(IPrincipal)
+      ...     id = '1'
+      ...     title = 'Mazinga'
+      ...     description = 'Mazinga Z'
+      ...     groups = ['superheroes', 'robots']
+      >>> class CaptainAmericaPrincipal:
+      ...     implements(IPrincipal)
+      ...     id = '2'
+      ...     title = 'CaptainAmerica'
+      ...     description = 'Captain America'
+      ...     groups = ['superheroes']
+      
+      Then we create instances of our fake principal classes:
+      
+      >>> goldrake = GoldrakePrincipal()
+      >>> mazinga = MazingaPrincipal()
+      >>> captainamerica = CaptainAmericaPrincipal()
+      >>> superheroesgroup = Group(id='superheroes', title='Superheroes group', description='The best superheroes', principalsids=['0', '1', '2'])
+      >>> robotsgroup = Group(id='robots', title='Robots group', description='The best robots', principalsids=['0', '1'])
+      
+      A groups folder contains groups:
+      
+      >>> notordinarypeople = GroupsFolder()
+      >>> notordinarypeople[superheroesgroup.id] = superheroesgroup
+      >>> notordinarypeople[robotsgroup.id] = robotsgroup
+      
+      We test that 'getGroupsForPrincipal' returns the groups the provided principal id belongs to:
+      
+      >>> notordinarypeople.getGroupsForPrincipal('1')
+      ['superheroes', 'robots']
+      >>> notordinarypeople.getGroupsForPrincipal('2')
+      ['superheroes']
+      >>> notordinarypeople.getGroupsForPrincipal('99')
+      []
+      
+      The 'getPrincipalsForGroup' returns the principals id contained in a group:
+      
+      >>> notordinarypeople.getPrincipalsForGroup('superheroes')
+      ['0', '1', '2']
+      >>> notordinarypeople.getPrincipalsForGroup('users')
+      []
+      
+      We also test removing a group:
+      
+      >>> del notordinarypeople['superheroes']
+      >>> print list(notordinarypeople.keys())
+      [u'robots']
+      >>> notordinarypeople.getGroupsForPrincipal('1')
+      ['robots']
+      >>> notordinarypeople.getGroupsForPrincipal('2')
+      []

Added: Zope3/trunk/src/zope/app/groupscontainer/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/groupscontainer/interfaces.py	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/groupscontainer/interfaces.py	2004-10-13 13:30:21 UTC (rev 28077)
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Zope Groups Folder Interface"""
+
+# $Id: interfaces.py 27237 2004-10-12 09:33:00 mriya3 $
+
+from zope.app.container.interfaces import IContained, IContainer
+from zope.app.container.constraints import ContainerTypesConstraint
+from zope.app.container.constraints import ItemTypePrecondition
+from zope.security.interfaces import IGroup
+from zope.schema import Field
+
+class IGroupsFolder(IContainer):
+       
+    def getGroupsForPrincipal(principalid):
+        """Get groups the given principal belongs to"""
+        
+    def getPrincipalsForGroup(groupid):
+        """Get principals which belong to the group"""
+        
+    def __setitem__(name, object):
+        """Adds a Group to the GroupFolder"""
+
+    __setitem__.precondition = ItemTypePrecondition(IGroup)
+
+class IGroupContained(IContained):
+    __parent__ = Field(
+             constraint = ContainerTypesConstraint(IGroupsFolder))
+

Added: Zope3/trunk/src/zope/app/groupscontainer/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/groupscontainer/tests.py	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/groupscontainer/tests.py	2004-10-13 13:30:21 UTC (rev 28077)
@@ -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.
+#
+##############################################################################
+"""Groups folder tests.
+
+$Id: tests.py 27237 2004-10-12 10:49:00 mriya3 $
+"""
+
+
+import unittest
+from zope.testing.doctest import DocFileSuite
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(DocFileSuite('groupsfolder.txt'))
+    return suite
+        
+    

Modified: Zope3/trunk/src/zope/app/security/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/security/configure.zcml	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/security/configure.zcml	2004-10-13 13:30:21 UTC (rev 28077)
@@ -13,7 +13,7 @@
   <vocabulary 
       name="Permissions"
       factory="zope.app.utility.vocabulary.UtilityVocabulary"
-      interface="zope.app.security.interfaces.IPermission" />
+      interface="zope.security.interfaces.IPermission" />
 
   <vocabulary
       name="Permission Ids"

Modified: Zope3/trunk/src/zope/app/security/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/security/interfaces.py	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/security/interfaces.py	2004-10-13 13:30:21 UTC (rev 28077)
@@ -18,48 +18,16 @@
 from zope.interface import Interface
 from zope.app.i18n import ZopeMessageIDFactory as _
 from zope.schema import Text, TextLine
+from zope.security.interfaces import IPrincipal, IPermission
 from zope.schema.interfaces import ISource
 
-class IPrincipal(Interface):
-    """Principals are security artifacts that execute actions in a security
-    environment.
 
-    The most common examples of principals include user and group objects.
-
-    It is likely that IPrincipal objects will have associated views
-    used to list principals in management interfaces. For example, a
-    system in which other meta-data are provided for principals might
-    extend IPrincipal and register a view for the extended interface
-    that displays the extended information. We'll probably want to
-    define a standard view name (e.g.  'inline_summary') for this
-    purpose.
-    """
-
-    id = TextLine(
-        title=_("Id"),
-        description=_("The unique identification of the principal."),
-        required=True,
-        readonly=True)
-
-    title = TextLine(
-        title=_("Title"),
-        description=_("The title of the principal. "
-                      "This is usually used in the UI."),
-        required=False)
-
-    description = Text(
-        title=_("Description"),
-        description=_("A detailed description of the principal."),
-        required=False)
-
-
 class IUnauthenticatedPrincipal(IPrincipal):
     """A principal that hasn't been authenticated.
 
     Authenticated principals are preferable to UnauthenticatedPrincipals.
     """
 
-
 class IAuthenticationService(Interface):
     """Provide support for establishing principals for requests.
 
@@ -177,25 +145,6 @@
         The realm argument is the name of the principal registry.
         """
 
-class IPermission(Interface):
-    """A permission object."""
-
-    id = TextLine(
-        title=_("Id"),
-        description=_("Id as which this permission will be known and used."),
-        readonly=True,
-        required=True)
-
-    title = TextLine(
-        title=_("Title"),
-        description=_("Provides a title for the permission."),
-        required=True)
-
-    description = Text(
-        title=_("Description"),
-        description=_("Provides a description for the permission."),
-        required=False)
-
-
 class IPrincipalSource(ISource):
     """A Source of Principal Ids"""
+

Modified: Zope3/trunk/src/zope/app/security/vocabulary.py
===================================================================
--- Zope3/trunk/src/zope/app/security/vocabulary.py	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/app/security/vocabulary.py	2004-10-13 13:30:21 UTC (rev 28077)
@@ -1,190 +1,190 @@
-##############################################################################
-#
-# 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.
-#
-##############################################################################
-"""Permission Id Vocabulary.
-
-This vocabulary provides permission IDs.
-
-$Id: $
-"""
-from zope.security.checker import CheckerPublic
-from zope.app import zapi
-from zope.interface import implements
-from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
-from zope.schema.interfaces import ISourceQueriables
-from zope.app.security.interfaces import IPermission
-from zope.app.component.localservice import queryNextService
-
-from interfaces import IPrincipalSource
-
-class PermissionIdsVocabulary(SimpleVocabulary):
-    """A vocabular of permission IDs.
-    
-    Term values are the permission ID strings except for 'zope.Public', which
-    is the global permission CheckerPublic.
-    
-    Term titles are the permission ID strings except for 'zope.Public', which
-    is shortened to 'Public'.
-    
-    Terms are sorted by title except for 'Public', which always appears as
-    the first term.
-
-    To illustrate, we need to register the permission IDs vocab:
-
-        >>> from zope.app.tests.placelesssetup import setUp, tearDown
-        >>> setUp()
-        >>> from zope.schema.vocabulary import getVocabularyRegistry
-        >>> registry = getVocabularyRegistry()
-        >>> registry.register('Permission Ids', PermissionIdsVocabulary)
-
-    We also need to register some sample permission utilities, including
-    the special permission 'zope.Public':
-    
-        >>> from zope.app.security.interfaces import IPermission
-        >>> from zope.app.security.permission import Permission
-        >>> from zope.app.tests import ztapi
-        >>> ztapi.provideUtility(IPermission, Permission('zope.Public'),
-        ...     'zope.Public')
-        >>> ztapi.provideUtility(IPermission, Permission('b'), 'b')
-        >>> ztapi.provideUtility(IPermission, Permission('a'), 'a')
-
-    We can now lookup these permissions using the vocabulary:
-    
-        >>> vocab = registry.get(None, 'Permission Ids')
-
-	The non-public permissions 'a' and 'b' are string values:
-	
-	    >>> vocab.getTermByToken('a').value
-	    u'a'
-	    >>> vocab.getTermByToken('b').value
-	    u'b'
-
-    However, the public permission value is CheckerPublic:
-    
-        >>> vocab.getTermByToken('zope.Public').value is CheckerPublic
-        True
-
-    and its title is shortened:
-
-        >>> vocab.getTermByToken('zope.Public').title
-        u'Public'
-
-    The terms are sorted by title except for the public permission, which is
-    listed first:
-    
-        >>> [term.title for term in vocab]
-        [u'Public', u'a', u'b']
-        
-        >>> tearDown()
-    """
-    def __init__(self, context):
-        terms = []
-        permissions = zapi.getUtilitiesFor(IPermission, context)
-        for name, permission in permissions:
-            if name == 'zope.Public':
-                terms.append(SimpleTerm(
-                    CheckerPublic, 'zope.Public', u'Public'))
-            else:
-                terms.append(SimpleTerm(name, name, name))
-        terms.sort(lambda lhs, rhs: \
-            lhs.title == u'Public' and -1 or cmp(lhs.title, rhs.title))
-        super(PermissionIdsVocabulary, self).__init__(terms)
-
-
-class PrincipalSource(object):
-    """Generic Principal Source"""
-    implements(IPrincipalSource, ISourceQueriables)
-
-    def __contains__(self, id):
-        """Test for the existence of a user.
-
-        We want to check whether the system knows about a particular
-        principal, which is referenced via its id. The source will go through
-        the most local authentication service to look for the
-        principal. Whether the service consults other services to give an
-        answer is up to the service itself.
-
-        First we need to create a dummy service that will return a user, if
-        the id is 'bob'.
-        
-        >>> class DummyService:
-        ...     def getPrincipal(self, id):
-        ...         if id == 'bob':
-        ...             return id
-
-        Since we do not want to bring up the entire component architecture, we
-        simply monkey patch the `getService()` method to always return our
-        dummy authentication service.
-
-        >>> temp = zapi.getService
-        >>> zapi.getService = lambda name: DummyService()
-
-        Now initialize the principal source and test the method
-
-        >>> source = PrincipalSource()
-        >>> 'jim' in source
-        False
-        >>> 'bob' in source
-        True
-
-        Now revert our patch.
-
-        >>> zapi.getService = temp
-        """
-        auth = zapi.getService(zapi.servicenames.Authentication)
-        principal = auth.getPrincipal(id)
-        return principal is not None
-
-    def getQueriables(self):
-        """Returns an iteratable of queriables. 
-
-        Queriables are responsible for providing interfaces to search for
-        principals by a set of given parameters (can be different for the
-        various queriables). This method will walk up through all of the
-        authentication services to look for queriables.
-
-        >>> class DummyService1:
-        ...     __parent__ = None
-        ...     def __repr__(self): return 'dummy1'
-        >>> dummy1 = DummyService1()
-        
-        >>> class DummyService2:
-        ...     implements(ISourceQueriables)
-        ...     __parent__ = None
-        ...     def getQueriables(self):
-        ...         return 1, 2, 3
-        >>> dummy2 = DummyService2()
-        
-        >>> from zope.app.component.localservice import testingNextService
-        >>> testingNextService(dummy1, dummy2, 'Authentication')
-        
-        >>> temp = zapi.getService
-        >>> zapi.getService = lambda name: dummy1
-
-        >>> source = PrincipalSource()
-        >>> list(source.getQueriables())
-        [dummy1, 1, 2, 3]
-
-        >>> zapi.getService = temp
-        """
-        auth = zapi.getService(zapi.servicenames.Authentication)
-        while True:
-            queriables = ISourceQueriables(auth, None)
-            if queriables is None:
-                yield auth
-            else:
-                for queriable in queriables.getQueriables():
-                    yield queriable
-            auth = queryNextService(auth, zapi.servicenames.Authentication)
-            if auth is None:
-                break
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Permission Id Vocabulary.
+
+This vocabulary provides permission IDs.
+
+$Id: $
+"""
+from zope.security.checker import CheckerPublic
+from zope.app import zapi
+from zope.interface import implements
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
+from zope.schema.interfaces import ISourceQueriables
+from zope.app.security.interfaces import IPermission
+from zope.app.component.localservice import queryNextService
+
+from interfaces import IPrincipalSource
+
+class PermissionIdsVocabulary(SimpleVocabulary):
+    """A vocabular of permission IDs.
+    
+    Term values are the permission ID strings except for 'zope.Public', which
+    is the global permission CheckerPublic.
+    
+    Term titles are the permission ID strings except for 'zope.Public', which
+    is shortened to 'Public'.
+    
+    Terms are sorted by title except for 'Public', which always appears as
+    the first term.
+
+    To illustrate, we need to register the permission IDs vocab:
+
+        >>> from zope.app.tests.placelesssetup import setUp, tearDown
+        >>> setUp()
+        >>> from zope.schema.vocabulary import getVocabularyRegistry
+        >>> registry = getVocabularyRegistry()
+        >>> registry.register('Permission Ids', PermissionIdsVocabulary)
+
+    We also need to register some sample permission utilities, including
+    the special permission 'zope.Public':
+    
+        >>> from zope.app.security.interfaces import IPermission
+        >>> from zope.app.security.permission import Permission
+        >>> from zope.app.tests import ztapi
+        >>> ztapi.provideUtility(IPermission, Permission('zope.Public'),
+        ...     'zope.Public')
+        >>> ztapi.provideUtility(IPermission, Permission('b'), 'b')
+        >>> ztapi.provideUtility(IPermission, Permission('a'), 'a')
+
+    We can now lookup these permissions using the vocabulary:
+    
+        >>> vocab = registry.get(None, 'Permission Ids')
+
+	The non-public permissions 'a' and 'b' are string values:
+	
+	    >>> vocab.getTermByToken('a').value
+	    u'a'
+	    >>> vocab.getTermByToken('b').value
+	    u'b'
+
+    However, the public permission value is CheckerPublic:
+    
+        >>> vocab.getTermByToken('zope.Public').value is CheckerPublic
+        True
+
+    and its title is shortened:
+
+        >>> vocab.getTermByToken('zope.Public').title
+        u'Public'
+
+    The terms are sorted by title except for the public permission, which is
+    listed first:
+    
+        >>> [term.title for term in vocab]
+        [u'Public', u'a', u'b']
+        
+        >>> tearDown()
+    """
+    def __init__(self, context):
+        terms = []
+        permissions = zapi.getUtilitiesFor(IPermission, context)
+        for name, permission in permissions:
+            if name == 'zope.Public':
+                terms.append(SimpleTerm(
+                    CheckerPublic, 'zope.Public', u'Public'))
+            else:
+                terms.append(SimpleTerm(name, name, name))
+        terms.sort(lambda lhs, rhs: \
+            lhs.title == u'Public' and -1 or cmp(lhs.title, rhs.title))
+        super(PermissionIdsVocabulary, self).__init__(terms)
+
+
+class PrincipalSource(object):
+    """Generic Principal Source"""
+    implements(IPrincipalSource, ISourceQueriables)
+
+    def __contains__(self, id):
+        """Test for the existence of a user.
+
+        We want to check whether the system knows about a particular
+        principal, which is referenced via its id. The source will go through
+        the most local authentication service to look for the
+        principal. Whether the service consults other services to give an
+        answer is up to the service itself.
+
+        First we need to create a dummy service that will return a user, if
+        the id is 'bob'.
+        
+        >>> class DummyService:
+        ...     def getPrincipal(self, id):
+        ...         if id == 'bob':
+        ...             return id
+
+        Since we do not want to bring up the entire component architecture, we
+        simply monkey patch the `getService()` method to always return our
+        dummy authentication service.
+
+        >>> temp = zapi.getService
+        >>> zapi.getService = lambda name: DummyService()
+
+        Now initialize the principal source and test the method
+
+        >>> source = PrincipalSource()
+        >>> 'jim' in source
+        False
+        >>> 'bob' in source
+        True
+
+        Now revert our patch.
+
+        >>> zapi.getService = temp
+        """
+        auth = zapi.getService(zapi.servicenames.Authentication)
+        principal = auth.getPrincipal(id)
+        return principal is not None
+
+    def getQueriables(self):
+        """Returns an iteratable of queriables. 
+
+        Queriables are responsible for providing interfaces to search for
+        principals by a set of given parameters (can be different for the
+        various queriables). This method will walk up through all of the
+        authentication services to look for queriables.
+
+        >>> class DummyService1:
+        ...     __parent__ = None
+        ...     def __repr__(self): return 'dummy1'
+        >>> dummy1 = DummyService1()
+        
+        >>> class DummyService2:
+        ...     implements(ISourceQueriables)
+        ...     __parent__ = None
+        ...     def getQueriables(self):
+        ...         return 1, 2, 3
+        >>> dummy2 = DummyService2()
+        
+        >>> from zope.app.component.localservice import testingNextService
+        >>> testingNextService(dummy1, dummy2, 'Authentication')
+        
+        >>> temp = zapi.getService
+        >>> zapi.getService = lambda name: dummy1
+
+        >>> source = PrincipalSource()
+        >>> list(source.getQueriables())
+        [dummy1, 1, 2, 3]
+
+        >>> zapi.getService = temp
+        """
+        auth = zapi.getService(zapi.servicenames.Authentication)
+        while True:
+            queriables = ISourceQueriables(auth, None)
+            if queriables is None:
+                yield auth
+            else:
+                for queriable in queriables.getQueriables():
+                    yield queriable
+            auth = queryNextService(auth, zapi.servicenames.Authentication)
+            if auth is None:
+                break

Modified: Zope3/trunk/src/zope/security/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/security/interfaces.py	2004-10-13 13:13:05 UTC (rev 28076)
+++ Zope3/trunk/src/zope/security/interfaces.py	2004-10-13 13:30:21 UTC (rev 28077)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2002 Zope Corporation and Contributors.
+# Copyright (c) 2004 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -18,6 +18,9 @@
 
 from zope.interface import Interface, Attribute, implements
 from zope.interface.common.interfaces import IAttributeError
+from zope.schema import Text, TextLine, List
+from zope.i18nmessageid import MessageFactory
+_ = MessageFactory('zope')
 
 class IUnauthorized(Interface):
     pass
@@ -231,4 +234,104 @@
 
         Does nothing if there is no interaction.
         """
+        
+class IPrincipal(Interface):
+    """Principals are security artifacts that execute actions in a security
+    environment.
 
+    The most common examples of principals include user and group objects.
+
+    It is likely that IPrincipal objects will have associated views
+    used to list principals in management interfaces. For example, a
+    system in which other meta-data are provided for principals might
+    extend IPrincipal and register a view for the extended interface
+    that displays the extended information. We'll probably want to
+    define a standard view name (e.g.  'inline_summary') for this
+    purpose.
+    """
+
+    id = TextLine(
+        title=_("Id"),
+        description=_("The unique identification of the principal."),
+        required=True,
+        readonly=True)
+
+    title = TextLine(
+        title=_("Title"),
+        description=_("The title of the principal. "
+                      "This is usually used in the UI."),
+        required=False)
+
+    description = Text(
+        title=_("Description"),
+        description=_("A detailed description of the principal."),
+        required=False)
+        
+    groups = List(
+        title=_("Groups"),
+        description=_("List of groups the principal belongs to"),
+        required=False)
+
+                
+class IPermission(Interface):
+    """A permission object."""
+
+    id = TextLine(
+        title=_("Id"),
+        description=_("Id as which this permission will be known and used."),
+        readonly=True,
+        required=True)
+
+    title = TextLine(
+        title=_("Title"),
+        description=_("Provides a title for the permission."),
+        required=True)
+
+    description = Text(
+        title=_("Description"),
+        description=_("Provides a description for the permission."),
+        required=False)
+        
+class IGroup(IPrincipal):
+    
+    id = TextLine(
+        title=_("Id"),
+        description=_("Id as which this group will be known and used."),
+        readonly=True,
+        required=True)
+
+    title = TextLine(
+        title=_("Title"),
+        description=_("Provides a title for the group."),
+        required=True)
+
+    description = Text(
+        title=_("Description"),
+        description=_("Provides a description for the group."),
+        required=False)
+
+    principals = List(
+        title=_("Principals"),
+        value_type=TextLine(),
+        description=_("List of principals which belong to the group"),
+        required=False)
+
+        
+class IPrincipalCreatedEvent(Interface):
+    """ Interface for principal created events"""
+    principal = Attribute(
+                    "The principal that was created")
+    
+class IGroupChangedEvent(Interface):
+    """ Interface for group changed event"""
+    group = Attribute(
+                    "The group that was changed")
+    
+    
+        
+    
+
+
+
+
+



More information about the Zope3-Checkins mailing list