[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