Skip to content

Commit

Permalink
No security tricks
Browse files Browse the repository at this point in the history
  • Loading branch information
trollfot committed Sep 26, 2012
1 parent 92d7f8b commit bdb4d15
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 73 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
recursive-include src/dolmen/menu *.txt *.py *.zcml *.pt
recursive-include src *.py *.zcml *.pt
recursive-include docs *.txt
12 changes: 10 additions & 2 deletions buildout.cfg
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
[buildout]
develop = .
parts = test
parts = test test-nosecurity
extensions = buildout.dumppickedversions
find-links = http://pypi.dolmen-project.org/find-links

[test]
recipe = zc.recipe.testrunner
eggs = dolmen.menu [test]
defaults = ['--tests-pattern', '^f?tests$', '-v',
dolmen.menu [security]
defaults = ['--tests-pattern', '^tests$', '-v',
'--auto-color', '--auto-progress']


[test-nosecurity]
recipe = zc.recipe.testrunner
eggs = dolmen.menu [test]
defaults = ['--tests-pattern', '^tests_nosecurity$', '-v',
'--auto-color', '--auto-progress']
12 changes: 8 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from os.path import join

name = 'dolmen.menu'
version = '2.1'
version = '2.2'
readme = open("README.txt").read()
history = open(join('docs', 'HISTORY.txt')).read()

Expand All @@ -16,22 +16,26 @@
'dolmen.template >= 0.2',
'dolmen.viewlet >= 0.4',
'grokcore.component',
'grokcore.security',
'zope.dottedname', # bug not declared by grokcore.security >= 1.6
'martian',
'setuptools',
'zope.component',
'zope.interface',
'zope.location',
'zope.schema',
'zope.security',
]

tests_require = [
'cromlech.browser [test]',
'zope.configuration',
]

tests_security = [
'dolmen.viewlet [security]',
'grokcore.security',
'zope.security',
]

setup(name=name,
version=version,
description='Dolmen menu components',
Expand All @@ -49,7 +53,7 @@
zip_safe=False,
tests_require=tests_require,
install_requires=install_requires,
extras_require={'test': tests_require},
extras_require={'test': tests_require, 'security': tests_security},
classifiers=[
'Environment :: Web Environment',
'Intended Audience :: Other Audience',
Expand Down
119 changes: 66 additions & 53 deletions src/dolmen/menu/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import urllib
import os

import grokcore.security
from grokcore.component import title, description, context
from grokcore.component import baseclass, adapter, implementer
from grokcore.component.util import sort_components
from cromlech.browser import ITemplate
from cromlech.i18n import ILanguage
from cromlech.browser import IRequest

import dolmen.viewlet
from dolmen.location import get_absolute_url
Expand All @@ -19,15 +19,50 @@
from zope.component import getAdapters, getMultiAdapter
from zope.interface import implements, Interface
from zope.schema.fieldproperty import FieldProperty
from zope.security import checkPermission
from zope.security.checker import CheckerPublic

try:
import zope.security

def isAvailable(viewlet):
try:
return viewlet.available
except AttributeError:
return True
def check_security(permission, component):
if permission == 'zope.Public':
# Translate public permission to CheckerPublic
permission = zope.security.checker.CheckerPublic
return zope.security.checkPermission(permission, component)

CHECKER = check_security
except ImportError:
CHECKER = None


def query_entries(context, request, view, menu, interface=IMenuEntry):
"""Query entries of the given menu :
* Queries the registry according to context, request, view, menu.
* Updates the components.
* Filters out the unavailable components.
* Returns an iterable of components.
"""
def isAvailable(component):
return bool(getattr(component, 'available', True))

def registry_components():
for name, entry in getAdapters(
(context, request, view, menu), interface):

success = True
if CHECKER is not None:
permission = entry.permission
if permission is not None:
success = CHECKER(permission, entry)

if success and isAvailable(entry):
if IMenuEntryViewlet.providedBy(entry):
entry.update()
yield entry

assert interface.isOrExtends(IMenuEntry)
assert IRequest.providedBy(request), "request must be an IRequest"
return registry_components()


class Menu(dolmen.viewlet.ViewletManager):
Expand All @@ -40,7 +75,6 @@ class Menu(dolmen.viewlet.ViewletManager):
implements(IMenu)
viewlets = []

entries = FieldProperty(IMenu['entries'])
menu_class = FieldProperty(IMenu['menu_class'])
entry_class = FieldProperty(IMenu['entry_class'])
context_url = FieldProperty(IMenu['context_url'])
Expand All @@ -55,51 +89,16 @@ def id(self):
return self.__class__.__name__.lower()
return component_name

@property
def entries(self):
return self.viewlets

def setMenuContext(self, item):
self.menu_context = item

def getMenuContext(self):
return self.menu_context or self.context

def _updateViewlets(self):
"""Doesn't fire events, like the original ViewletManager, on purpose.
"""
for viewlet in self.viewlets:
if IMenuEntryViewlet.providedBy(viewlet):
viewlet.update()

def filter(self, viewlets):
"""Iterator which filters out menu items based on
availability and user authorizations
"""
for name, viewlet in viewlets:
if getattr(viewlet, 'available', True):
permission = viewlet.permission
if permission == 'zope.Public':
# Translate public permission to CheckerPublic
permission = CheckerPublic
if checkPermission(permission, self.context) and \
isAvailable(viewlet):
yield name, viewlet

def render(self):
"""Template is taken from the template attribute or searching
for an adapter to ITemplate for menu and request
"""
template = getattr(self, 'template', None)
if template is None:
template = getMultiAdapter((self, self.request), ITemplate)
return template.render(
self, target_language=self.target_language, **self.namespace())

def get_menu_entries(self):
# Find all content providers for the region
viewlets = getAdapters(
(self.getMenuContext(), self.request, self.view, self),
IMenuEntry)
for item in self.filter(viewlets):
yield item

@property
def target_language(self):
return ILanguage(self.request, None)
Expand All @@ -114,9 +113,19 @@ def update(self):
# Get the MenuContext and calculate its url
self.context_url = get_absolute_url(menu_context, self.request)

viewlets = self.get_menu_entries()
self.viewlets = sort_components([entry for name, entry in viewlets])
self._updateViewlets()
# Get the viewlets, sort them and update them
self.viewlets = sort_components(list(query_entries(
menu_context, self.request, self.view, self)))

def render(self):
"""Template is taken from the template attribute or searching
for an adapter to ITemplate for menu and request
"""
template = getattr(self, 'template', None)
if template is None:
template = getMultiAdapter((self, self.request), ITemplate)
return template.render(
self, target_language=self.target_language, **self.namespace())


class Entry(Location):
Expand All @@ -128,8 +137,8 @@ class Entry(Location):
baseclass()
implements(IMenuEntryViewlet)
context(Interface)
params = None

params = None
available = True

def __init__(self, context, request, view, manager):
Expand Down Expand Up @@ -192,7 +201,11 @@ def title(self):

@property
def permission(self):
return grokcore.security.require.bind().get(self)
try:
import grokcore.security
return grokcore.security.require.bind().get(self)
except ImportError:
return None

@property
def description(self):
Expand Down
15 changes: 12 additions & 3 deletions src/dolmen/menu/declarations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import types
import zope.interface
import zope.component
import grokcore.security

from martian.error import GrokImportError
from martian.util import frame_is_module
Expand All @@ -15,6 +14,16 @@
from dolmen.menu.interfaces import IMenu, IMenuEntry
from grokcore.component import order, title, name, description, context

try:
import grokcore.security
SecurityGetter = lambda:grokcore.security.require.bind(default='zope.View')

except ImportError:

class SecurityGetter(object):
def get(self, factory):
return None


def get_default_name(factory, module=None, **data):
return factory.__name__.lower()
Expand All @@ -27,7 +36,7 @@ def get_default_name(factory, module=None, **data):
'context': context.bind(default=zope.interface.Interface),
'request': request.bind(default=IRequest),
'view': view.bind(default=zope.interface.Interface),
'permission': grokcore.security.require.bind(default='zope.View'),
'permission': SecurityGetter(),
'order': order.bind(),
}

Expand Down Expand Up @@ -109,7 +118,7 @@ def factory(self, component, menu, **args):
return (component, menu, args)


class menuentry:
class menuentry(object):
"""
Decorator meant to be use on a function to declare it as a menu entry
Expand Down
9 changes: 3 additions & 6 deletions src/dolmen/menu/interfaces.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-

from zope import schema # XXX Keep it ? (inpact setup.py if not)
from zope.interface import Interface
from zope.interface import Interface, Attribute
from zope.location import ILocation
from dolmen.viewlet import IViewletManager, IViewlet
from zope.security.zcml import Permission


class IMenuEntry(Interface):
Expand All @@ -14,10 +13,8 @@ class IMenuEntry(Interface):
title=u"Identifier",
required=True)

permission = Permission(
title=u"Permission",
description=u"Permission required to use this component.",
required=True)
permission = Attribute(
u"Permission required to use this component.")

title = schema.TextLine(
required=True,
Expand Down
5 changes: 1 addition & 4 deletions src/dolmen/menu/tests/test_multidecorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,7 @@ def __init__(self, context, request):
self.context = context
self.request = request

def update(self, *args, **kwargs):
pass

def render(self, *args, **kwargs):
def render(self):
return u"I'm a simple view"


Expand Down
1 change: 1 addition & 0 deletions src/dolmen/menu/tests_nosecurity/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# a test module
Loading

0 comments on commit bdb4d15

Please sign in to comment.