Skip to content

Commit

Permalink
Merged revisions 37911, 37979 from the trunk:
Browse files Browse the repository at this point in the history
    Grant views cleaned up
  • Loading branch information
Dmitry Vasiliev committed Aug 17, 2005
1 parent 171b9e6 commit 723d27f
Show file tree
Hide file tree
Showing 4 changed files with 700 additions and 0 deletions.
176 changes: 176 additions & 0 deletions authentication.py
@@ -0,0 +1,176 @@
##############################################################################
#
# 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.
#
##############################################################################
"""Pluggable Authentication Utility implementation
$Id$
"""
import zope.interface

from zope import component
from zope.schema.interfaces import ISourceQueriables
from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
from zope.app.location.interfaces import ILocation
from zope.app.component import queryNextUtility
from zope.app.component.site import SiteManagementFolder

from zope.app.authentication import interfaces


class PluggableAuthentication(SiteManagementFolder):

zope.interface.implements(
IAuthentication,
interfaces.IPluggableAuthentication,
ISourceQueriables)

authenticatorPlugins = ()
credentialsPlugins = ()

def __init__(self, prefix=''):
super(PluggableAuthentication, self).__init__()
self.prefix = prefix

def authenticate(self, request):
authenticatorPlugins = [
component.queryUtility(interfaces.IAuthenticatorPlugin,
name, context=self)
for name in self.authenticatorPlugins]
for name in self.credentialsPlugins:
credplugin = component.queryUtility(
interfaces.ICredentialsPlugin, name, context=self)
if credplugin is None:
continue
credentials = credplugin.extractCredentials(request)
for authplugin in authenticatorPlugins:
if authplugin is None:
continue
info = authplugin.authenticateCredentials(credentials)
if info is None:
continue
principal = component.getMultiAdapter((info, request),
interfaces.IAuthenticatedPrincipalFactory)(self)
principal.id = self.prefix + info.id
return principal
return None

def getPrincipal(self, id):
if not id.startswith(self.prefix):
next = queryNextUtility(self, IAuthentication)
if next is None:
raise PrincipalLookupError(id)
return next.getPrincipal(id)
id = id[len(self.prefix):]
for name in self.authenticatorPlugins:
authplugin = component.queryUtility(
interfaces.IAuthenticatorPlugin, name, context=self)
if authplugin is None:
continue
info = authplugin.principalInfo(id)
if info is None:
continue
principal = interfaces.IFoundPrincipalFactory(info)(self)
principal.id = self.prefix + info.id
return principal
next = queryNextUtility(self, IAuthentication)
if next is not None:
return next.getPrincipal(self.prefix + id)
raise PrincipalLookupError(id)

def getQueriables(self):
for name in self.authenticatorPlugins:
authplugin = component.queryUtility(
interfaces.IAuthenticatorPlugin, name, context=self)
if authplugin is None:
continue
queriable = component.queryMultiAdapter((authplugin, self),
interfaces.IQueriableAuthenticator)
if queriable is not None:
yield name, queriable

def unauthenticatedPrincipal(self):
return None

def unauthorized(self, id, request):
challengeProtocol = None

for name in self.credentialsPlugins:
credplugin = component.queryUtility(interfaces.ICredentialsPlugin,
name)
if credplugin is None:
continue
protocol = getattr(credplugin, 'challengeProtocol', None)
if challengeProtocol is None or protocol == challengeProtocol:
if credplugin.challenge(request):
if protocol is None:
return
elif challengeProtocol is None:
challengeProtocol = protocol

if challengeProtocol is None:
next = queryNextUtility(self, IAuthentication)
if next is not None:
next.unauthorized(id, request)

def logout(self, request):
challengeProtocol = None

for name in self.credentialsPlugins:
credplugin = component.queryUtility(interfaces.ICredentialsPlugin,
name)
if credplugin is None:
continue
protocol = getattr(credplugin, 'challengeProtocol', None)
if challengeProtocol is None or protocol == challengeProtocol:
if credplugin.logout(request):
if protocol is None:
return
elif challengeProtocol is None:
challengeProtocol = protocol

if challengeProtocol is None:
next = queryNextUtility(self, IAuthentication)
if next is not None:
next.logout(request)


class QuerySchemaSearchAdapter(object):
"""Performs schema-based principal searches on behalf of a PAU.
Delegates the search to the adapted authenticator (which also provides
IQuerySchemaSearch) and prepends the PAU prefix to the resulting principal
IDs.
"""
component.adapts(
interfaces.IQuerySchemaSearch,
interfaces.IPluggableAuthentication)

zope.interface.implements(
interfaces.IQueriableAuthenticator,
interfaces.IQuerySchemaSearch,
ILocation)

def __init__(self, authplugin, pau):
if ILocation.providedBy(authplugin):
self.__parent__ = authplugin.__parent__
self.__name__ = authplugin.__name__
else:
self.__parent__ = pau
self.__name__ = ""
self.authplugin = authplugin
self.pau = pau
self.schema = authplugin.schema

def search(self, query, start=None, batch_size=None):
for id in self.authplugin.search(query, start, batch_size):
yield self.pau.prefix + id
110 changes: 110 additions & 0 deletions browser/schemasearch.py
@@ -0,0 +1,110 @@
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Search interface for queriables.
$Id$
"""
__docformat__ = "reStructuredText"

from zope.interface import implements
from zope.i18n import translate
from zope.schema import getFieldsInOrder
from zope.app.zapi import getName, getPath
from zope.app.form.utility import setUpWidgets, getWidgetsData
from zope.app.form.interfaces import IInputWidget
from zope.app.form.browser.interfaces import ISourceQueryView
from zope.app.i18n import ZopeMessageIDFactory as _


search_label = _('search-button', 'Search')
source_label = _(u"Source path")
source_title = _(u"Path to the source utility")

class QuerySchemaSearchView(object):
implements(ISourceQueryView)

def __init__(self, context, request):
self.context = context
self.request = request

def render(self, name):
schema = self.context.schema
sourcename = getName(self.context)
sourcepath = getPath(self.context)
setUpWidgets(self, schema, IInputWidget, prefix=name+'.field')
html = []

# add sub title for source search field
html.append('<h4>%s</h4>' % sourcename)

# start row for path display field
html.append('<div class="row">')

# for each source add path of source
html.append(' <div class="label">')
label = translate(source_label, context=self.request)
title = translate(source_title, context=self.request)
html.append(' <label for="%s" title="%s">' % (sourcename, title))
html.append(' %s' % label)
html.append(' </label>')
html.append(' </div>')
html.append(' <div class="field">')
html.append(' %s' % sourcepath)
html.append(' </div>')
html.append('</div>')

# start row for search fields
html.append('<div class="row">')

for field_name, field in getFieldsInOrder(schema):
widget = getattr(self, field_name+'_widget')

# for each field add label...
html.append(' <div class="label">')
html.append(' <label for="%s" title="%s">'
% (widget.name, widget.hint))
html.append(' %s' % widget.label)
html.append(' </label>')
html.append(' </div>')

# ...and field widget
html.append(' <div class="field">')
html.append(' %s' % widget())

if widget.error():
html.append(' <div class="error">')
html.append(' %s' % widget.error())
html.append(' </div>')
html.append(' </div>')
# end row
html.append('</div>')

# add search button for search fields
html.append('<div class="row">')
html.append(' <div class="field">')
html.append(' <input type="submit" name="%s" value="%s" />'
% (name+'.search',
translate(search_label, context=self.request)))
html.append(' </div>')
html.append('</div>')

return '\n'.join(html)

def results(self, name):
if not (name+'.search' in self.request):
return None
schema = self.context.schema
setUpWidgets(self, schema, IInputWidget, prefix=name+'.field')
data = getWidgetsData(self, schema)
return self.context.search(data)
104 changes: 104 additions & 0 deletions browser/schemasearch.txt
@@ -0,0 +1,104 @@
The Query View for Schema Search Plugins
========================================

Placefull setup for making the search plugin IPhysicallyLocatable:

>>> from zope.app.testing import ztapi
>>> from zope.schema.interfaces import ITextLine
>>> from zope.app.form.browser import TextWidget
>>> from zope.app.form.interfaces import IInputWidget
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
>>> ztapi.browserView(ITextLine, '', TextWidget, providing=IInputWidget)

If a plugin supports `IQuerySchemaSearch`:

>>> from zope.interface import Interface
>>> import zope.schema
>>> class ISearchCriteria(Interface):
... search = zope.schema.TextLine(title=u"Search String")

>>> from zope.interface import implements
>>> class MySearchPlugin:
... __name__ = 'searchplugin'
... __parent__ = site
... schema = ISearchCriteria
... data = ['foo', 'bar', 'blah']
...
... def get(self, id):
... if id in self.data:
... return {}
...
... def search(self, query, start=None, batch_size=None):
... search = query.get('search')
... if search is not None:
... i = 0
... n = 0
... for value in self.data:
... if search in value:
... if not ((start is not None and i < start)
... or
... (batch_size is not None and n > batch_size)):
... yield value

then we can get a view:

>>> from zope.app.authentication.browser.schemasearch \
... import QuerySchemaSearchView
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> view = QuerySchemaSearchView(MySearchPlugin(), request)

This allows us to render a search form.

>>> print view.render('test') # doctest: +NORMALIZE_WHITESPACE
<h4>searchplugin</h4>
<div class="row">
<div class="label">
<label for="searchplugin" title="Path to the source utility">
Source path
</label>
</div>
<div class="field">
/searchplugin
</div>
</div>
<div class="row">
<div class="label">
<label for="test.field.search" title="">
Search String
</label>
</div>
<div class="field">
<input class="textType" id="test.field.search" name="test.field.search"
size="20" type="text" value="" />
</div>
</div>
<div class="row">
<div class="field">
<input type="submit" name="test.search" value="Search" />
</div>
</div>

If we ask for results:

>>> view.results('test')

We don't get any, since we did not provide any. But if we give input:

>>> request.form['test.field.search'] = 'a'

we still don't get any:

>>> view.results('test')

because we did not press the button. So let's press the button:

>>> request.form['test.search'] = 'Search'

so that we now get results (!):

>>> list(view.results('test'))
['bar', 'blah']

>>> placefulTearDown()

0 comments on commit 723d27f

Please sign in to comment.