[Checkins] SVN: zc.security/trunk/ Released to zope.org repository.

Jim Fulton jim at zope.com
Tue Jun 27 18:00:53 EDT 2006


Log message for revision 68872:
  Released to zope.org repository.

Changed:
  A   zc.security/trunk/README.txt
  A   zc.security/trunk/setup.py
  A   zc.security/trunk/src/
  A   zc.security/trunk/src/zc/
  A   zc.security/trunk/src/zc/__init__.py
  A   zc.security/trunk/src/zc/security/
  A   zc.security/trunk/src/zc/security/__init__.py
  A   zc.security/trunk/src/zc/security/browser/
  A   zc.security/trunk/src/zc/security/browser/__init__.py
  A   zc.security/trunk/src/zc/security/browser/configure.zcml
  A   zc.security/trunk/src/zc/security/browser/queryview.pt
  A   zc.security/trunk/src/zc/security/browser/queryview.py
  A   zc.security/trunk/src/zc/security/browser/queryview.txt
  A   zc.security/trunk/src/zc/security/browser/tests.py
  A   zc.security/trunk/src/zc/security/configure.zcml
  A   zc.security/trunk/src/zc/security/i18n.py
  A   zc.security/trunk/src/zc/security/interfaces.py
  A   zc.security/trunk/src/zc/security/search.py
  A   zc.security/trunk/src/zc/security/search.txt
  A   zc.security/trunk/src/zc/security/tests.py

-=-
Added: zc.security/trunk/README.txt
===================================================================
--- zc.security/trunk/README.txt	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/README.txt	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,5 @@
+Enhanced UI for searching for prinicipals
+=========================================
+
+This package provides some Zope 3 user interfaces for searching for
+principals managed by the pluggable authentication utility.


Property changes on: zc.security/trunk/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.security/trunk/setup.py
===================================================================
--- zc.security/trunk/setup.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/setup.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,31 @@
+from setuptools import setup, find_packages
+
+setup(
+    name = "zc.security",
+    version = "1.0",
+    author = "Zope Corporation",
+    author_email = "zope3-dev at zope.org",
+    description = "Principal-searching UI for Zope 3 Pluggable Authentication",
+    license = "ZPL 2.1",
+    keywords = "zope3 security",
+    url='http://svn.zope.org/zc.sharing',
+    classifiers = [
+        'Development Status :: 3 - Alpha',
+        "License :: OSI Approved :: Zope Public License",
+        "Framework :: Zope :: UI",
+        ],
+    
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['zc'],
+    install_requires = [
+       'zope.testing',
+       'setuptools',
+       # XXX leaving out most of the zope 3 dependencies for now,
+       # since Zope 3 hasn't been packages yet.
+       ],
+    dependency_links = ['http://download.zope.org/distribution/'],
+    )
+
+


Property changes on: zc.security/trunk/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/__init__.py
===================================================================
--- zc.security/trunk/src/zc/__init__.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/__init__.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,6 @@
+# namespace package boilerplate
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError, e:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)


Property changes on: zc.security/trunk/src/zc/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/__init__.py
===================================================================
--- zc.security/trunk/src/zc/security/__init__.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/__init__.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1 @@
+#


Property changes on: zc.security/trunk/src/zc/security/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/browser/__init__.py
===================================================================
--- zc.security/trunk/src/zc/security/browser/__init__.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/browser/__init__.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1 @@
+#


Property changes on: zc.security/trunk/src/zc/security/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/browser/configure.zcml
===================================================================
--- zc.security/trunk/src/zc/security/browser/configure.zcml	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/browser/configure.zcml	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,10 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="zc.security">
+
+    <adapter factory=".queryview.SourceTerms" />
+    <adapter factory=".queryview.SimpleUserSourceQueryView" />
+    <adapter factory=".queryview.SimpleGroupSourceQueryView" />
+    <adapter factory=".queryview.SimplePrincipalSourceQueryView" />
+
+</configure>


Property changes on: zc.security/trunk/src/zc/security/browser/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/browser/queryview.pt
===================================================================
--- zc.security/trunk/src/zc/security/browser/queryview.pt	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/browser/queryview.pt	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,23 @@
+<div i18n:domain="zc.security">
+  <div tal:condition="python: view.searchstring == ''"
+       i18n:translate="">Please enter a search string.</div>
+  <div class="row">
+    <span class="label">
+      <label for="" tal:attributes="for options/field_name"
+             i18n:translate="source_query_view_search"
+             >Browse</label>
+    </span>
+    <span class="field">
+      <input type="text" name="" tal:attributes="
+             name options/field_name; id options/field_name;
+             value python: request.get(options['field_name'], nothing)" />
+    </span>
+  </div>
+  <div class="row">
+    <div class="field">
+      <input type="submit" name="" value="Search"
+             tal:attributes="name options/button_name"
+             i18n:attributes="value search_button" class="submit" />
+    </div>
+  </div>
+</div>


Property changes on: zc.security/trunk/src/zc/security/browser/queryview.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/browser/queryview.py
===================================================================
--- zc.security/trunk/src/zc/security/browser/queryview.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/browser/queryview.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,119 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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.
+#
+##############################################################################
+"""query views for shared sources
+
+$Id$
+"""
+import zope.app.form.browser.interfaces
+import zope.publisher.interfaces.browser
+
+from zope import component, interface, i18n
+from zope.app import zapi
+from zope.app import pagetemplate
+from zope.schema.interfaces import ITitledTokenizedTerm
+
+from zc.security.i18n import _
+from zc.security.interfaces import ISimpleUserSource
+from zc.security.interfaces import ISimpleUserSearch
+from zc.security.interfaces import ISimpleGroupSource
+from zc.security.interfaces import ISimpleGroupSearch
+from zc.security.interfaces import ISimplePrincipalSource
+from zc.security.interfaces import ISimplePrincipalSearch
+
+SEARCH_FIELD_NAME = _('source_query_view_search', 'Search String')
+SEARCH_BUTTON = _('search-button', 'Search')
+
+class SimplePrincipalSourceQueryBaseView(object):
+    interface.implements(zope.app.form.browser.interfaces.ISourceQueryView)
+
+    def __init__(self, source, request):
+        self.context = source
+        self.request = request
+
+    _render = pagetemplate.ViewPageTemplateFile('queryview.pt')
+    def render(self, name):
+        field_name = name + '.searchstring'
+        self.searchstring = self.request.get(field_name)
+        return self._render(field_name = field_name,
+                            button_name = name + '.search')
+
+    def results(self, name):
+        if not (name+'.search' in self.request):
+            return None
+        searchstring = self.request[name+'.searchstring']
+        if not searchstring:
+            return None
+        principals = zapi.principals()
+        return self.search(principals, searchstring, 0, 999999999999)
+
+    def search(self, principals, searchstring, start, size):
+        raise NotImplementedError('override in subclasses')
+
+
+class SimpleUserSourceQueryView(SimplePrincipalSourceQueryBaseView):
+
+    component.adapts(ISimpleUserSource,
+                     zope.publisher.interfaces.browser.IBrowserRequest)
+
+    def search(self, principals, searchstring, start, size):
+        search = ISimpleUserSearch(principals)
+        return search.searchUsers(searchstring, start, size)
+
+
+class SimpleGroupSourceQueryView(SimplePrincipalSourceQueryBaseView):
+
+    component.adapts(ISimpleGroupSource,
+                     zope.publisher.interfaces.browser.IBrowserRequest)
+
+    def search(self, principals, searchstring, start, size):
+        search = ISimpleGroupSearch(principals)
+        return search.searchGroups(searchstring, start, size)
+
+
+class SimplePrincipalSourceQueryView(SimplePrincipalSourceQueryBaseView):
+
+    component.adapts(ISimplePrincipalSource,
+                     zope.publisher.interfaces.browser.IBrowserRequest)
+
+    def search(self, principals, searchstring, start, size):
+        search = ISimplePrincipalSearch(principals)
+        return search.searchPrincipals(searchstring, start, size)
+
+
+class Term:
+    zope.interface.implements(ITitledTokenizedTerm)
+
+    def __init__(self, title, token):
+        self.title = title
+        self.token = token
+
+
+class SourceTerms:
+    """Term and value support needed by query widgets."""
+
+    zope.interface.implements(zope.app.form.browser.interfaces.ITerms)
+    component.adapts(ISimplePrincipalSource,
+                     zope.publisher.interfaces.browser.IBrowserRequest)
+
+    def __init__(self, source, request):
+        pass
+
+    def getTerm(self, value):
+        principal = zapi.principals().getPrincipal(value)
+        token = value.encode('base64').strip()
+        return Term(principal.title, token)
+
+    def getValue(self, token):
+        return token.decode('base64')


Property changes on: zc.security/trunk/src/zc/security/browser/queryview.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/browser/queryview.txt
===================================================================
--- zc.security/trunk/src/zc/security/browser/queryview.txt	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/browser/queryview.txt	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,116 @@
+---------
+queryview
+---------
+
+The queryview module holds query views for security-related sources. 
+The ISourceQueryView interface is found in
+zope.app.form.browser.interfaces, and defines two methods, render and
+results.
+
+SimplePrincipalSourceQueryView
+------------------------------
+
+The SimplePrincipalSourceQueryView is useful for finding principals with
+only a single search string.  It does not allow further filtering of the
+results.  It relies on the installed authentication utility (such as the
+pluggable authentication utility) being adaptable to
+zc.security.interfaces.ISimplePrincipalSource.
+
+In order to instantiate a query view, one must have a source and a browser
+request.  In the current implementation of this view, the source is not used,
+so we can use None as the source.
+
+    >>> from zc.security.browser import queryview
+    >>> from zope.publisher.browser import TestRequest
+    >>> request = TestRequest()
+    >>> v = queryview.SimplePrincipalSourceQueryView(None, request)
+
+The view exposes two methods, as required by ISourceQueryView.  They both need
+to be passed a prefix to use for showing and finding their form entries.
+First, let's render a queryview.
+
+    >>> v.render('myprefix')
+    u'...<input type="text" name="myprefix.searchstring"...
+    <input type="submit" name="myprefix.search"...
+    value="Search"...'
+
+
+The rendering should keep values previously entered for it.  Therefore, if the
+user enters a search string, then it is included in following redraws.
+
+    >>> request.form['myprefix.searchstring'] = 'foo'
+    >>> v.render('myprefix')
+    u'...<input type="text" name="myprefix.searchstring"...
+    value="foo"...'
+
+The results method does not fire unless the button value is in the request,
+even if there is a value for the search string.
+
+    >>> print v.results('myprefix')
+    None
+
+In order to perform a successful search, we need to register an adapter from
+IAuthentication to ISimplePrincipalSearch.  This, for example, is a standard
+adapter:
+
+    >>> import zope.component
+    >>> import zc.security
+    >>> import zc.security.search
+    >>> import zc.security.interfaces
+    >>> import zope.interface
+    >>> from zope.app.security.interfaces import IAuthentication
+
+    >>> class FakeAuthenticationUtility:
+    ...     zope.interface.implements(IAuthentication)
+    >>> fake = FakeAuthenticationUtility()
+
+    >>> zope.component.provideUtility(fake, IAuthentication)
+
+    >>> zope.component.provideAdapter(
+    ...     factory=zc.security.search.SimplePrincipalSearch,
+    ...     adapts=[zope.app.security.interfaces.IAuthentication],
+    ...     provides=zc.security.interfaces.ISimplePrincipalSearch,
+    ...     )
+
+    >>> request.form['myprefix.search'] = 'yes'
+    >>> v.results('myprefix')
+    <generator object...>
+
+
+SimpleUserSourceQueryView and SimpleGroupSourceQueryView
+--------------------------------------------------------
+
+While SimplePrincipalSourceQueryView searches for both users and groups,
+SimpleUserSourceQueryView and SimpleGroupSourceQueryView search for only
+users or only groups, respectively.
+
+    >>> from zc.security.interfaces import ISimpleUserSearch
+    >>> class FakeUserSearch(object):
+    ...     zope.component.adapts(IAuthentication)
+    ...     zope.interface.implements(ISimpleUserSearch)
+    ...     def __init__(self, context):
+    ...         pass
+    ...     def searchUsers(self, searchstring, start, size):
+    ...         yield 'user1'
+    ...         yield 'user2'
+    >>> zope.component.provideAdapter(FakeUserSearch)
+
+    >>> v = queryview.SimpleUserSourceQueryView(None, request)
+    >>> list(v.results('myprefix'))
+    ['user1', 'user2']
+
+    >>> from zc.security.interfaces import ISimpleGroupSearch
+    >>> class FakeGroupSearch(object):
+    ...     zope.component.adapts(IAuthentication)
+    ...     zope.interface.implements(ISimpleGroupSearch)
+    ...     def __init__(self, context):
+    ...         pass
+    ...     def searchGroups(self, searchstring, start, size):
+    ...         yield 'group1'
+    ...         yield 'group2'
+    >>> zope.component.provideAdapter(FakeGroupSearch)
+
+    >>> v = queryview.SimpleGroupSourceQueryView(None, request)
+    >>> list(v.results('myprefix'))
+    ['group1', 'group2']
+


Property changes on: zc.security/trunk/src/zc/security/browser/queryview.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/browser/tests.py
===================================================================
--- zc.security/trunk/src/zc/security/browser/tests.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/browser/tests.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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$
+"""
+import unittest
+
+from zope.testing import doctest
+from zope.app.testing import placelesssetup
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite(
+            'queryview.txt',
+            setUp=placelesssetup.setUp,
+            tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS,
+           ),
+       ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: zc.security/trunk/src/zc/security/browser/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/configure.zcml
===================================================================
--- zc.security/trunk/src/zc/security/configure.zcml	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/configure.zcml	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,16 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="zc.security">
+
+<adapter factory=".search.PASimpleGroupSearch" />
+<adapter factory=".search.PASimpleUserSearch" />
+<adapter factory=".search.GroupFolderSimpleGroupSearch" />
+<adapter factory=".search.UserFolderSimpleUserSearch" />
+<adapter factory=".search.UserRegistrySimpleUserSearch" />
+<adapter factory=".search.UserRegistrySimpleGroupSearch" />
+<adapter factory=".search.SimplePrincipalSearch" />
+
+<include package=".browser" />
+
+</configure>
+


Property changes on: zc.security/trunk/src/zc/security/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/i18n.py
===================================================================
--- zc.security/trunk/src/zc/security/i18n.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/i18n.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""I18N support for shortcuts.
+
+This defines a `MessageFactory` for the I18N domain for the security
+package.  This is normally used with this import::
+
+  from i18n import MessageFactory as _
+
+The factory is then used normally.  Two examples::
+
+  text = _('some internationalized text')
+  text = _('helpful-descriptive-message-id', 'default text')
+"""
+__docformat__ = "reStructuredText"
+
+
+from zope import i18nmessageid
+
+MessageFactory = _ = i18nmessageid.MessageFactory("zc.security")
+


Property changes on: zc.security/trunk/src/zc/security/i18n.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/interfaces.py
===================================================================
--- zc.security/trunk/src/zc/security/interfaces.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/interfaces.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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.
+#
+##############################################################################
+import zope.interface
+import zope.schema.interfaces
+import zope.app.security.vocabulary
+
+class ISimplePrincipalSource(zope.schema.interfaces.ISource):
+    """Supports simple querying."""
+
+class ISimpleUserSource(ISimplePrincipalSource):
+    """Supports simple querying."""
+
+class ISimpleGroupSource(ISimplePrincipalSource):
+    """Supports simple querying."""
+
+
+# XXX the search interfaces below should commit to searching by title, or
+# have some other specified contract about sorting.
+
+class ISimpleUserSearch(zope.interface.Interface):
+    """Simple search limited to users
+    """
+
+    def searchUsers(filter, start, size):
+        """Search for principals
+
+        Return an iterable of principal ids.
+
+        If a filter is supplied, principals are restricted to
+        principals that "match" the filter string.  Typically,
+        matching is based on subscrings of text such as principal
+        titles, descriptions, etc.
+
+        Results should be ordered in some consistent fashion, to make
+        batching work in a reasonable way.
+
+        No more than that number of
+        principal ids will be returned.
+
+        """
+
+class ISimpleGroupSearch(zope.interface.Interface):
+    """Simple search limited to users
+    """
+
+    def searchGroups(filter, start, size):
+        """Search for principals
+
+        Return an iterable of principal ids.
+
+        If a filter is supplied, principals are restricted to
+        principals that "match" the filter string.  Typically,
+        matching is based on subscrings of text such as principal
+        titles, descriptions, etc.
+
+        Results should be ordered in some consistent fashion, to make
+        batching work in a reasonable way.
+
+        No more than that number of
+        principal ids will be returned.
+
+        """
+
+class ISimplePrincipalSearch(zope.interface.Interface):
+    """Simple search of all principals
+    """
+
+    def searchPrincipals(filter, start, size):
+        """Search for principals
+
+        Return an iterable of principal ids.
+
+        If a filter is supplied, principals are restricted to
+        principals that "match" the filter string.  Typically,
+        matching is based on subscrings of text such as principal
+        titles, descriptions, etc.
+
+        Results should be ordered in some consistent fashion, to make
+        batching work in a reasonable way.
+
+        No more than that number of
+        principal ids will be returned.
+
+        """
+


Property changes on: zc.security/trunk/src/zc/security/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/search.py
===================================================================
--- zc.security/trunk/src/zc/security/search.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/search.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,208 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Pluggable authentication adapters
+
+$Id$
+"""
+
+import zope.component
+import zope.interface
+from zope.security.interfaces import IGroup
+import zope.app.authentication.interfaces
+import zope.app.authentication.authentication
+import zope.app.authentication.groupfolder
+import zope.app.authentication.principalfolder
+from zope.app.security import principalregistry
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import PrincipalLookupError
+from zope.app.component import queryNextUtility
+from zc.security import interfaces
+
+class PASimpleSearch:
+
+    zope.component.adapts(
+        zope.app.authentication.interfaces.IPluggableAuthentication)
+
+    def __init__(self, context):
+        self.context = context
+
+    def searchPrincipals(self, filter, start, size):
+        if size <= 0:
+            return
+
+        prefix = self.context.prefix
+
+        n = 0
+        for name, plugin in self.context.getAuthenticatorPlugins():
+            searcher = self.type(plugin, None)
+            if searcher is None:
+                continue
+
+            sz = size + start - n
+            if sz <= 0:
+                return
+
+            search = getattr(searcher, self.meth)
+            for principal_id in search(filter, 0, sz):
+                if n >= start:
+                    yield prefix + principal_id
+                n += 1
+
+        next = self.context
+        while 1:
+            next = queryNextUtility(next, IAuthentication)
+            if next is None:
+                return
+            searcher = self.type(next, None)
+            if searcher is None:
+                continue
+
+            sz = size + start - n
+            if sz <= 0:
+                return
+                
+            search = getattr(searcher, self.meth)
+            for principal_id in search(filter, 0, sz):
+                if n >= start:
+                    yield principal_id
+                n += 1
+            return
+
+
+class PASimpleGroupSearch(PASimpleSearch):
+    type = interfaces.ISimpleGroupSearch
+    zope.interface.implements(type)
+    meth = 'searchGroups'
+    searchGroups = PASimpleSearch.searchPrincipals
+
+class PASimpleUserSearch(PASimpleSearch):
+    type = interfaces.ISimpleUserSearch
+    zope.interface.implements(type)
+    meth = 'searchUsers'
+    searchUsers = PASimpleSearch.searchPrincipals
+
+class GroupFolderSimpleGroupSearch:
+    
+    def __init__(self, context):
+        self.context = context
+
+    def searchGroups(self, filter, start, size):
+        return self.context.search({'search': filter}, start, size)
+
+    zope.component.adapts(zope.app.authentication.groupfolder.IGroupFolder)
+    zope.interface.implements(interfaces.ISimpleGroupSearch)
+
+class UserFolderSimpleUserSearch:
+
+    zope.component.adapts(
+        zope.app.authentication.principalfolder.PrincipalFolder)
+    zope.interface.implements(interfaces.ISimpleUserSearch)
+    
+    def __init__(self, context):
+        self.context = context
+
+    def searchUsers(self, filter, start, size):
+        return self.context.search({'search': filter}, start, size)
+
+class UserRegistrySimpleUserSearch:
+
+    zope.component.adapts(principalregistry.PrincipalRegistry)
+    zope.interface.implements(interfaces.ISimpleUserSearch)
+    
+    def __init__(self, context):
+        self.context = context
+
+    def searchUsers(self, filter, start, size):
+        filter = filter.lower()
+        result = [p.id for p in list(self.context.getPrincipals(''))
+                  if (filter in p.getLogin().lower()
+                      or
+                      filter in p.title.lower()
+                      or
+                      filter in p.description.lower()
+                      ) and not IGroup.providedBy(p)]
+        result.sort()
+        return result[start:start+size]
+
+class UserRegistrySimpleGroupSearch(UserRegistrySimpleUserSearch):
+    zope.interface.implementsOnly(interfaces.ISimpleGroupSearch)
+
+    def searchGroups(self, filter, start, size):
+        filter = filter.lower()
+        result = [p.id for p in list(self.context.getPrincipals(''))
+                  if (filter in p.getLogin().lower()
+                      or
+                      filter in p.title.lower()
+                      or
+                      filter in p.description.lower()
+                      ) and IGroup.providedBy(p)]
+        result.sort()
+        return result[start:start+size]
+
+class SimplePrincipalSource(zope.app.security.vocabulary.PrincipalSource):
+    zope.interface.implementsOnly(interfaces.ISimplePrincipalSource)
+
+class SimpleUserSource(zope.app.security.vocabulary.PrincipalSource):
+    zope.interface.implementsOnly(interfaces.ISimpleUserSource)
+
+    def __contains__(self, id):
+        auth = zope.component.getUtility(IAuthentication)
+        try:
+            return not IGroup.providedBy(auth.getPrincipal(id))
+        except PrincipalLookupError:
+            return False
+
+class SimpleGroupSource(zope.app.security.vocabulary.PrincipalSource):
+    zope.interface.implementsOnly(interfaces.ISimpleGroupSource)
+
+    def __contains__(self, id):
+        auth = zope.component.getUtility(IAuthentication)
+        try:
+            return IGroup.providedBy(auth.getPrincipal(id))
+        except PrincipalLookupError:
+            return False
+
+class SimplePrincipalSearch:
+
+    zope.interface.implements(interfaces.ISimplePrincipalSearch)
+    zope.component.adapts(IAuthentication)
+
+    def __init__(self, context):
+        self.context = context
+
+    def searchPrincipals(self, filter, start, size):
+        count = 0
+
+        user_search = interfaces.ISimpleUserSearch(self.context, None)
+        if user_search is not None:
+            users = user_search.searchUsers(filter, 0, size+start)
+            for user in users:
+                if count >= start:
+                    yield user
+                count += 1
+
+            if count-start == size:
+                return
+
+        group_search = interfaces.ISimpleGroupSearch(self.context, None)
+        if group_search is not None:
+            groups = group_search.searchGroups(filter, 0, size+start-count)
+            for group in groups:
+                if count >= start:
+                    yield group
+                count += 1
+
+            if count-start == size:
+                return


Property changes on: zc.security/trunk/src/zc/security/search.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/search.txt
===================================================================
--- zc.security/trunk/src/zc/security/search.txt	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/search.txt	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,546 @@
+Search support adapters
+=======================
+
+We provide adapters to support searching for groups or users.
+
+Pluggable Authentication adapter for Searching for groups
+---------------------------------------------------------
+
+We provide an adapter for the pluggable authentication utility for
+searching for groups.  It searches the utilities search plugins for
+plugins that can be adapted to `ISimpleGroupSearch`.  It also searches
+higher-level authentication utilities.
+
+Let's look at an example.  We'll provide a fake pluggable
+authentication utility that simply has some authenticator plugins:
+
+    >>> import zope.interface
+    >>> import zope.component
+    >>> from zope.app.security.interfaces import IAuthentication
+    >>> from zope.app.security.interfaces import PrincipalLookupError
+    >>> from zope.app.authentication.interfaces import (
+    ...     IPluggableAuthentication, IAuthenticatorPlugin)
+    >>> class PA:
+    ...     prefix = 'test.'
+    ...     zope.interface.implements(IPluggableAuthentication,
+    ...                               IAuthentication)
+    ...     authenticatorPlugins = ('users', 'groups', 'dummy',
+    ...                             'moreusers', 'moregroups', 'missing')
+    ...     def getAuthenticatorPlugins(self):
+    ...         for nm in self.authenticatorPlugins:
+    ...             p = zope.component.queryUtility(
+    ...                 IAuthenticatorPlugin, name=nm)
+    ...             if p is not None:
+    ...                 yield nm, p
+    ...     def getPrincipal(self, principal_id):
+    ...         for nm, p in self.getAuthenticatorPlugins():
+    ...             try:
+    ...                 return p.getPrincipal(principal_id[len(nm)+1:])
+    ...             except PrincipalLookupError:
+    ...                 pass
+    ...         raise PrincipalLookupError
+
+We define a Dummy utility that will be ignored by the searcher:
+
+    >>> from zope.app.authentication.interfaces import IAuthenticatorPlugin
+    >>> class DummyPrincipalSearchPlugin:
+    ...     zope.interface.implements(IAuthenticatorPlugin)
+    ...     def getPrincipal(self, principal_id):
+    ...         raise PrincipalLookupError
+
+    >>> zope.component.provideUtility(
+    ...     DummyPrincipalSearchPlugin(), IAuthenticatorPlugin, 'dummy')
+
+We define a utility that supports group searching:
+
+    >>> class Principals:
+    ...     zope.interface.implements(IAuthenticatorPlugin)
+    ...
+    ...     def __init__(self, *principals):
+    ...         self.principals = principals
+    ...
+    ...     def searchPrincipals(self, filter, start=0, size=None):
+    ...         principals = self.principals
+    ...         principals = [p for p in principals if filter in p]
+    ...         return principals[start:start+size]
+    ...
+    ...     def getPrincipal(self, principal_id):
+    ...         if principal_id in self.principals:
+    ...             return self.principal_factory()
+    ...         raise PrincipalLookupError
+
+    >>> from zope.security.interfaces import IGroup
+    >>> class Group(object):
+    ...     zope.interface.implements(IGroup)
+
+    >>> from zc.security.interfaces import ISimpleGroupSearch
+    >>> class Groups(Principals):
+    ...     zope.interface.implements(ISimpleGroupSearch)
+    ...     searchGroups = Principals.searchPrincipals
+    ...     principal_factory = Group
+
+    >>> groups = Groups(
+    ...    'root', 'bin', 'daemon', 'sys', 'adm', 'tty', 'disk', 'lp', 'mem',
+    ...    'kmem', 'wheel', 'mail', 'news', 'uucp', 'man', 'games', 'gopher',
+    ...    )
+    >>> zope.component.provideUtility(groups, IAuthenticatorPlugin, 'groups')
+
+    >>> moregroups = Groups(
+    ...    'dip', 'ftp', 'lock', 'nobody', 'users', 'rpm', 'floppy', 'vcsa',
+    ...    'utmp', 'rpc', 'rpcuser', 'nfsnobody', 'mailnull', 'smmsp',
+    ...    'pcap', 'dbus', 'xfs', 'ntp', 'gdm',
+    ...    )
+    >>> zope.component.provideUtility(moregroups, IAuthenticatorPlugin, 'moregroups')
+
+And we define a utility that provides user searching:
+
+    >>> from zope.security.interfaces import IPrincipal
+    >>> class User(object):
+    ...     zope.interface.implements(IPrincipal)
+
+    >>> from zc.security.interfaces import ISimpleUserSearch
+    >>> class Users(Principals):
+    ...     zope.interface.implements(ISimpleUserSearch)
+    ...     searchUsers = Principals.searchPrincipals
+    ...     principal_factory = User
+
+    >>> users = Users(
+    ...    'zalman', 'zaltana', 'zamir', 'zamora', 'zan',
+    ...    'zander', 'zandra', 'zane', 'zanna', 'zanta',
+    ...    'zanthe', 'zara', 'zareb', 'zared', 'zareh',
+    ...    )
+    >>> zope.component.provideUtility(users, IAuthenticatorPlugin, 'users')
+
+    >>> moreusers = Users(
+    ...    'zorina', 'zorion', 'zsa', 'zubaida', 'zubeda',
+    ...    'zubin', 'zudora', 'zula', 'zuleika', 'zulema',
+    ...    'zulu', 'zuna', 'zuri', 'zuriel', 'zurina',
+    ...    'zuwena', 'zuzana', 'zuzela', 'zwi', 'zyta', 'zytka',
+    ...    )
+    >>> zope.component.provideUtility(
+    ...     moreusers, IAuthenticatorPlugin, 'moreusers')
+
+Finally, we'll provide some utilities in a hierarchy.
+
+    >>> pa = PA()
+
+    >>> class DummyAuth:
+    ...     zope.interface.implements(IAuthentication)
+
+    >>> class SearchableDummyAuth(Groups, Users, DummyAuth):
+    ...     pass
+
+    >>> dummyauth = DummyAuth()
+    >>> searchabledummyauth = SearchableDummyAuth(
+    ...    'baba', 'baback', 'babette', 'baby', 'bach',
+    ...    'bade', 'baden', 'badu', 'baeddan', 'bahari',
+    ...    'bailey', 'baina', 'baird', 'bairn', 'baka',
+    ...    'baldasarre', 'balin', 'ballard', 'ballari',
+    ...    'balthasar', 'bambi', 'ban', 'banagher',
+    ...    'bandele', 'banji', 'banyan', 'bao', 'baqer',
+    ...    'barak', 'barb', 'barbara', 'barbie', 'barbod',
+    ...    'barbra', 'barclay', 'bardia', 'barid', 'barke',
+    ...    )
+
+    >>> searchabledummyauth2 = SearchableDummyAuth(
+    ...    'barnabas', 'barnard', 'barney', 'barny', 'baron',
+    ...    'barr', 'barrett', 'barrington', 'barry', 'bart',
+    ...    'barth', 'bartholemew', 'bartholomew', 'barto',
+    ...    'barton', 'baruch', 'bary', 'base', 'bash',
+    ...    'basil', 'bast', 'bastien', 'bat', 'bathsheba',
+    ...    'baxter', 'bayard', 'bayle', 'baylee', 'bazyli',
+    ...    'bea', 'beata', 'beate', 'beatrice', 'beatrix',
+    ...    )
+
+    >>> from zope.app.component.testing import testingNextUtility
+    >>> testingNextUtility(pa, dummyauth, IAuthentication)
+    >>> testingNextUtility(dummyauth, searchabledummyauth, IAuthentication)
+    >>> testingNextUtility(searchabledummyauth, searchabledummyauth2,
+    ...                    IAuthentication)
+
+Nooooow, we can try out some searches and see if we get the right results.
+
+    >>> from zc.security.search import PASimpleGroupSearch
+    >>> search = PASimpleGroupSearch(pa)
+
+First, we'll get "all" of the groups:
+
+    >>> list(search.searchGroups('', 0, 1000))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.root', 'test.bin', 'test.daemon', 'test.sys', 'test.adm',
+    'test.tty', 'test.disk', 'test.lp', 'test.mem', 'test.kmem',
+    'test.wheel', 'test.mail', 'test.news', 'test.uucp', 'test.man',
+    'test.games', 'test.gopher', 'test.dip', 'test.ftp', 'test.lock',
+    'test.nobody', 'test.users', 'test.rpm', 'test.floppy',
+    'test.vcsa', 'test.utmp', 'test.rpc', 'test.rpcuser',
+    'test.nfsnobody', 'test.mailnull', 'test.smmsp', 'test.pcap',
+    'test.dbus', 'test.xfs', 'test.ntp', 'test.gdm', 'baba', 'baback',
+    'babette', 'baby', 'bach', 'bade', 'baden', 'badu', 'baeddan',
+    'bahari', 'bailey', 'baina', 'baird', 'bairn', 'baka',
+    'baldasarre', 'balin', 'ballard', 'ballari', 'balthasar', 'bambi',
+    'ban', 'banagher', 'bandele', 'banji', 'banyan', 'bao', 'baqer',
+    'barak', 'barb', 'barbara', 'barbie', 'barbod', 'barbra',
+    'barclay', 'bardia', 'barid', 'barke']
+
+Note that we didn't get any ids from searchabledummyauth2. This is because it
+is the responsibility of an ISimpleGroupSearch adapter of an authentication
+utility to delegate to higher utilities.  Because searchabledummyauth didn't
+delegate, we don't get any values from searchabledummyauth2.
+
+Now, let's try batching the results:
+
+    >>> list(search.searchGroups('', 0, 7))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.root', 'test.bin', 'test.daemon', 'test.sys', 'test.adm',
+     'test.tty', 'test.disk']
+
+    >>> list(search.searchGroups('', 14, 7))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.man', 'test.games', 'test.gopher', 'test.dip', 'test.ftp',
+     'test.lock', 'test.nobody']
+
+    >>> list(search.searchGroups('', 35, 7))
+    ['test.gdm', 'baba', 'baback', 'babette', 'baby', 'bach', 'bade']
+
+    >>> list(search.searchGroups('', 70, 7))
+    ['barclay', 'bardia', 'barid', 'barke']
+
+And searching:
+
+    >>> list(search.searchGroups('n', 0, 1000))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.bin', 'test.daemon', 'test.news', 'test.man',
+    'test.nobody', 'test.nfsnobody', 'test.mailnull', 'test.ntp',
+    'baden', 'baeddan', 'baina', 'bairn', 'balin', 'ban', 'banagher',
+    'bandele', 'banji', 'banyan']
+
+and searching with batching:
+
+    >>> list(search.searchGroups('n', 0, 7))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.bin', 'test.daemon', 'test.news', 'test.man',
+    'test.nobody', 'test.nfsnobody', 'test.mailnull']
+
+    >>> list(search.searchGroups('n', 7, 7))
+    ['test.ntp', 'baden', 'baeddan', 'baina', 'bairn', 'balin', 'ban']
+
+Group Folder adapter for searching for groups
+---------------------------------------------
+
+A group folder adapter supports simple searching:
+
+    >>> from zope.app.authentication import groupfolder
+    >>> groups = groupfolder.GroupFolder('group.')
+
+    >>> groups['g1'] = groupfolder.GroupInformation("Group 1")
+    >>> groups['g2'] = groupfolder.GroupInformation("Group Two")
+    >>> groups['g3'] = groupfolder.GroupInformation("Others")
+    >>> groups['g4'] = groupfolder.GroupInformation("System")
+    >>> groups['g5'] = groupfolder.GroupInformation("SHOUTERS")
+    >>> groups['g6'] = groupfolder.GroupInformation("kids")
+    >>> groups['g7'] = groupfolder.GroupInformation("Dudes")
+    >>> groups['g8'] = groupfolder.GroupInformation("Foo")
+    >>> groups['g9'] = groupfolder.GroupInformation("Bar")
+
+    >>> from zc.security.search import GroupFolderSimpleGroupSearch
+    >>> search = GroupFolderSimpleGroupSearch(groups)
+    >>> list(search.searchGroups('', 0, 100))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    [u'group.g1', u'group.g2', u'group.g3', u'group.g4', u'group.g5',
+     u'group.g6', u'group.g7', u'group.g8', u'group.g9']
+
+    >>> list(search.searchGroups('', 5, 10))
+    [u'group.g6', u'group.g7', u'group.g8', u'group.g9']
+
+    >>> list(search.searchGroups('', 2, 4))
+    [u'group.g3', u'group.g4', u'group.g5', u'group.g6']
+
+    >>> list(search.searchGroups('s', 0, 100))
+    [u'group.g3', u'group.g4', u'group.g5', u'group.g6', u'group.g7']
+
+    >>> list(search.searchGroups('s', 2, 2))
+    [u'group.g3', u'group.g4']
+
+Pluggable Authentication adapter for Searching for users
+--------------------------------------------------------
+
+There's a similar adapter for searching for users:
+
+
+    >>> from zc.security.search import PASimpleUserSearch
+    >>> search = PASimpleUserSearch(pa)
+
+First, we'll get "all" of the users:
+
+    >>> list(search.searchUsers('', 0, 1000))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.zalman', 'test.zaltana', 'test.zamir', 'test.zamora',
+    'test.zan', 'test.zander', 'test.zandra', 'test.zane',
+    'test.zanna', 'test.zanta', 'test.zanthe', 'test.zara',
+    'test.zareb', 'test.zared', 'test.zareh', 'test.zorina',
+    'test.zorion', 'test.zsa', 'test.zubaida', 'test.zubeda',
+    'test.zubin', 'test.zudora', 'test.zula', 'test.zuleika',
+    'test.zulema', 'test.zulu', 'test.zuna', 'test.zuri',
+    'test.zuriel', 'test.zurina', 'test.zuwena', 'test.zuzana',
+    'test.zuzela', 'test.zwi', 'test.zyta', 'test.zytka', 'baba',
+    'baback', 'babette', 'baby', 'bach', 'bade', 'baden', 'badu',
+    'baeddan', 'bahari', 'bailey', 'baina', 'baird', 'bairn', 'baka',
+    'baldasarre', 'balin', 'ballard', 'ballari', 'balthasar', 'bambi',
+    'ban', 'banagher', 'bandele', 'banji', 'banyan', 'bao', 'baqer',
+    'barak', 'barb', 'barbara', 'barbie', 'barbod', 'barbra',
+    'barclay', 'bardia', 'barid', 'barke']
+
+Note that we didn't get any ids from searchabledummyauth2. This is because it
+is the responsibility of an ISimpleUserSearch adapter of an authentication
+utility to delegate to higher utilities.  Because searchabledummyauth didn't
+delegate, we don't get any values from searchabledummyauth2.
+
+Now, let's try batching the results:
+
+    >>> list(search.searchUsers('', 0, 7))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.zalman', 'test.zaltana', 'test.zamir', 'test.zamora',
+    'test.zan', 'test.zander', 'test.zandra']
+
+
+    >>> list(search.searchUsers('', 14, 7))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.zareh', 'test.zorina', 'test.zorion', 'test.zsa',
+    'test.zubaida', 'test.zubeda', 'test.zubin']
+
+    >>> list(search.searchUsers('', 35, 7))
+    ['test.zytka', 'baba', 'baback', 'babette', 'baby', 'bach', 'bade']
+
+    >>> list(search.searchUsers('', 70, 7))
+    ['barclay', 'bardia', 'barid', 'barke']
+
+And searching:
+
+    >>> list(search.searchUsers('n', 0, 1000))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.zalman', 'test.zaltana', 'test.zan', 'test.zander',
+    'test.zandra', 'test.zane', 'test.zanna', 'test.zanta',
+    'test.zanthe', 'test.zorina', 'test.zorion', 'test.zubin',
+    'test.zuna', 'test.zurina', 'test.zuwena', 'test.zuzana', 'baden',
+    'baeddan', 'baina', 'bairn', 'balin', 'ban', 'banagher',
+    'bandele', 'banji', 'banyan']
+
+
+and searching with batching:
+
+    >>> list(search.searchUsers('n', 0, 7))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.zalman', 'test.zaltana', 'test.zan', 'test.zander',
+    'test.zandra', 'test.zane', 'test.zanna']
+
+    >>> list(search.searchUsers('n', 7, 7))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['test.zanta', 'test.zanthe', 'test.zorina', 'test.zorion',
+    'test.zubin', 'test.zuna', 'test.zurina']
+
+
+Principals Folder adapter for searching for users
+-------------------------------------------------
+
+A principal folder adapter supports simple searching:
+
+    >>> from zope.app.authentication import principalfolder
+    >>> users = principalfolder.PrincipalFolder('users.')
+
+    >>> for name in [
+    ...    'zalman', 'zaltana', 'zamir', 'zamora', 'zan',
+    ...    'zander', 'zandra', 'zane', 'zanna', 'zanta',
+    ...    'zanthe', 'zara', 'zareb', 'zared', 'zareh',
+    ...    ]:
+    ...    users[name] = principalfolder.InternalPrincipal(
+    ...       name, '123', name.capitalize())
+
+    >>> from zc.security.search import UserFolderSimpleUserSearch
+    >>> search = UserFolderSimpleUserSearch(users)
+    >>> list(search.searchUsers('', 0, 100))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    [u'users.zalman', u'users.zaltana', u'users.zamir', u'users.zamora',
+     u'users.zan', u'users.zander', u'users.zandra', u'users.zane',
+     u'users.zanna', u'users.zanta', u'users.zanthe', u'users.zara',
+     u'users.zareb', u'users.zared', u'users.zareh']
+
+    >>> list(search.searchUsers('', 5, 10))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    [u'users.zander', u'users.zandra', u'users.zane', u'users.zanna',
+     u'users.zanta', u'users.zanthe', u'users.zara', u'users.zareb',
+     u'users.zared', u'users.zareh']
+
+    >>> list(search.searchUsers('', 2, 4))
+    [u'users.zamir', u'users.zamora', u'users.zan', u'users.zander']
+
+    >>> list(search.searchUsers('n', 0, 100))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    [u'users.zalman', u'users.zaltana', u'users.zan', u'users.zander',
+     u'users.zandra', u'users.zane', u'users.zanna', u'users.zanta',
+     u'users.zanthe']
+
+    >>> list(search.searchUsers('n', 2, 4))
+    [u'users.zan', u'users.zander', u'users.zandra', u'users.zane']
+
+ZCML Principal Registry adapters for searching for users and groups
+-------------------------------------------------------------------
+
+A principal registry adapter supports simple searching
+
+    >>> from zope.app.security import principalregistry
+    >>> users = principalregistry.PrincipalRegistry()
+
+    >>> for name in [
+    ...    'zalman', 'zaltana', 'zamir', 'zamora', 'zan',
+    ...    'zander', 'zandra', 'zane', 'zanna', 'zanta',
+    ...    'zanthe', 'zara', 'zareb', 'zared', 'zareh',
+    ...    ]:
+    ...    _ = users.definePrincipal(name, name.capitalize(), '', name, '123')
+    >>> everybody = principalregistry.EverybodyGroup('e', 'Everybody', '')
+    >>> users.registerGroup(everybody)
+    >>> auth = principalregistry.AuthenticatedGroup('a', 'Authenticated', '')
+    >>> users.registerGroup(auth)
+    >>> unauth = principalregistry.UnauthenticatedGroup(
+    ...     'u', 'Unauthenticated', '')
+    >>> users.registerGroup(unauth)
+
+
+    >>> from zc.security.search import UserRegistrySimpleUserSearch
+    >>> search = UserRegistrySimpleUserSearch(users)
+    >>> list(search.searchUsers('', 0, 100))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['zalman', 'zaltana', 'zamir', 'zamora',
+     'zan', 'zander', 'zandra', 'zane',
+     'zanna', 'zanta', 'zanthe', 'zara',
+     'zareb', 'zared', 'zareh']
+
+    >>> list(search.searchUsers('', 5, 10))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['zander', 'zandra', 'zane', 'zanna',
+     'zanta', 'zanthe', 'zara', 'zareb',
+     'zared', 'zareh']
+
+    >>> list(search.searchUsers('', 2, 4))
+    ['zamir', 'zamora', 'zan', 'zander']
+
+    >>> list(search.searchUsers('n', 0, 100))
+    ... # doctest: +NORMALIZE_WHITESPACE
+    ['zalman', 'zaltana', 'zan', 'zander',
+     'zandra', 'zane', 'zanna', 'zanta',
+     'zanthe']
+
+    >>> list(search.searchUsers('n', 2, 4))
+    ['zan', 'zander', 'zandra', 'zane']
+
+    >>> from zc.security.search import UserRegistrySimpleGroupSearch
+    >>> search = UserRegistrySimpleGroupSearch(users)
+    >>> list(search.searchGroups('', 0, 100))
+    ['a', 'e', 'u']
+
+    >>> list(search.searchGroups('', 1, 1))
+    ['e']
+
+    >>> list(search.searchGroups('ev', 0, 100))
+    ['e']
+
+
+Searching all principals
+------------------------
+
+In addition to searching for users and groups alone as shown above,
+another interface allows searching together for both groups and users:
+all principals.
+
+This is currently implemented with an adapter that unions the result
+of searches for users and groups.  For testing purposes, our
+authentication service will already provide the necessary interfaces,
+eliminating the necessity of registering any adapters.
+
+    >>> class DummySimpleUserSearchAuthentication:
+    ...     zope.interface.implements(
+    ...         ISimpleUserSearch, ISimpleGroupSearch)
+    ...     def searchUsers(self, filter, start, size):
+    ...         data = ['uAx', 'uBy', 'uCx', 'uDy', 'uEx']
+    ...         if filter:
+    ...             data = [d for d in data if filter in d]
+    ...         for u in data[start:start+size]:
+    ...             yield u
+    ...     def searchGroups(self, filter, start, size):
+    ...         data = ['gAx', 'gBy', 'gCx', 'gDy', 'gEx']
+    ...         if filter:
+    ...             data = [d for d in data if filter in d]
+    ...         for u in data[start:start+size]:
+    ...             yield u
+    ...
+    >>> auth = DummySimpleUserSearchAuthentication()
+    >>> from zc.security.search import SimplePrincipalSearch
+    >>> search = SimplePrincipalSearch(auth)
+
+First, we can get all principals.
+
+    >>> list(search.searchPrincipals('', 0, 1000))
+    ['uAx', 'uBy', 'uCx', 'uDy', 'uEx', 'gAx', 'gBy', 'gCx', 'gDy', 'gEx']
+
+Passing in a filter works as shown above: pass in a string, with semantics as
+determined by the plugin.
+
+    >>> list(search.searchPrincipals('x', 0, 1000))
+    ['uAx', 'uCx', 'uEx', 'gAx', 'gCx', 'gEx']
+
+Here are some examples of the batching semantics combined with a filter.
+
+    >>> list(search.searchPrincipals('x', 0, 4))
+    ['uAx', 'uCx', 'uEx', 'gAx']
+    >>> list(search.searchPrincipals('x', 2, 3))
+    ['uEx', 'gAx', 'gCx']
+    >>> list(search.searchPrincipals('x', 0, 3))
+    ['uAx', 'uCx', 'uEx']
+    >>> list(search.searchPrincipals('x', 3, 1000))
+    ['gAx', 'gCx', 'gEx']
+
+These are similar examples without an active filter.
+
+    >>> list(search.searchPrincipals('', 0, 8))
+    ['uAx', 'uBy', 'uCx', 'uDy', 'uEx', 'gAx', 'gBy', 'gCx']
+    >>> list(search.searchPrincipals('', 2, 6))
+    ['uCx', 'uDy', 'uEx', 'gAx', 'gBy', 'gCx']
+    >>> list(search.searchPrincipals('', 0, 5))
+    ['uAx', 'uBy', 'uCx', 'uDy', 'uEx']
+    >>> list(search.searchPrincipals('', 5, 5))
+    ['gAx', 'gBy', 'gCx', 'gDy', 'gEx']
+
+
+Sources
+=======
+
+There are sources defined for use in schemas:
+
+    >>> from zc.security.search import SimplePrincipalSource
+    >>> from zc.security.search import SimpleUserSource
+    >>> from zc.security.search import SimpleGroupSource
+    >>> principal_source = SimplePrincipalSource()
+    >>> user_source = SimpleUserSource()
+    >>> group_source = SimpleGroupSource()
+
+    >>> zope.component.provideUtility(pa, IAuthentication)
+
+SimplePrincipalSearch contains all users and groups
+
+    >>> 'users.zared' in principal_source
+    True
+    >>> 'groups.wheel' in principal_source
+    True
+
+SimpleUserSource contains only users
+
+    >>> 'users.zared' in user_source
+    True
+    >>> 'groups.wheel' in user_source
+    False
+
+SimpleGroupSource contains only groups
+
+    >>> 'users.zared' in group_source
+    False
+    >>> 'groups.wheel' in group_source
+    True


Property changes on: zc.security/trunk/src/zc/security/search.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.security/trunk/src/zc/security/tests.py
===================================================================
--- zc.security/trunk/src/zc/security/tests.py	2006-06-27 21:54:01 UTC (rev 68871)
+++ zc.security/trunk/src/zc/security/tests.py	2006-06-27 22:00:52 UTC (rev 68872)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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$
+"""
+import unittest
+
+from zope.testing import doctest
+from zope.app.testing import placelesssetup
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite(
+            'search.txt',
+            setUp=placelesssetup.setUp,
+            tearDown=placelesssetup.tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE+doctest.ELLIPSIS,
+           ),
+       ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: zc.security/trunk/src/zc/security/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Checkins mailing list