allow better control of create actions by checking 'AssignAsParent': check action permissions + related fixes

This commit is contained in:
Helmut Merz 2013-01-28 16:00:06 +01:00
parent d9a0f39f06
commit 344a1d81e5
12 changed files with 56 additions and 30 deletions

View file

@ -2,8 +2,6 @@
loops - Linked Objects for Organization and Processing Services
===============================================================
($Id$)
The loops platform consists up of three basic types of objects:
(1) concepts: simple interconnected objects usually representing
@ -612,7 +610,7 @@ Actions
>>> view.controller = Controller(view, request)
>>> #view.setupController()
>>> actions = view.getActions('portlet')
>>> actions = view.getAllowedActions('portlet')
>>> len(actions)
2
@ -849,7 +847,7 @@ In order to provide suitable links for viewing or editing a target you may
ask a view which view and edit actions it supports. We directly use the
target object's view here:
>>> actions = view.virtualTarget.getActions('object', page=view)
>>> actions = view.virtualTarget.getAllowedActions('object', page=view)
>>> #actions[0].url
'http://127.0.0.1/loops/views/m1/m11/m111/.target23'

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
# Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -129,6 +129,7 @@ actions.register('edit_object', 'portlet', DialogAction,
viewName='edit_object.html',
dialogName='edit',
prerequisites=['registerDojoEditor'],
permission='zope.ManageContent',
)
actions.register('edit_concept', 'portlet', DialogAction,
@ -137,6 +138,7 @@ actions.register('edit_concept', 'portlet', DialogAction,
viewName='edit_concept.html',
dialogName='edit',
prerequisites=['registerDojoEditor'],
permission='zope.ManageContent',
)
actions.register('create_concept', 'portlet', DialogAction,
@ -146,6 +148,7 @@ actions.register('create_concept', 'portlet', DialogAction,
dialogName='createConcept',
qualifier='create_concept',
innerForm='inner_concept_form.html',
permission='loops.AssignAsParent',
)
actions.register('create_subtype', 'portlet', DialogAction,
@ -155,4 +158,5 @@ actions.register('create_subtype', 'portlet', DialogAction,
dialogName='createConcept',
qualifier='subtype',
innerForm='inner_concept_form.html',
permission='loops.AssignAsParent',
)

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
# Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -45,7 +45,7 @@ from zope.publisher.browser import applySkin
from zope.publisher.interfaces.browser import IBrowserSkinType, IBrowserView
from zope import schema
from zope.schema.vocabulary import SimpleTerm
from zope.security import canAccess, checkPermission
from zope.security import canAccess
from zope.security.interfaces import ForbiddenAttribute, Unauthorized
from zope.security.proxy import removeSecurityProxy
from zope.traversing.browser import absoluteURL
@ -67,6 +67,7 @@ from loops.i18n.browser import I18NView
from loops.interfaces import IResource, IView, INode, ITypeConcept
from loops.organize.tracking import access
from loops.resource import Resource
from loops.security.common import checkPermission
from loops.security.common import canAccessObject, canListObject, canWriteObject
from loops.type import ITypeConcept
from loops import util
@ -706,6 +707,16 @@ class BaseView(GenericView, I18NView):
"""
return []
def getAllowedActions(self, category='object', page=None, target=None):
result = []
for act in self.getActions(category, page=page, target=target):
if act.permission is not None:
ctx = (target is not None and target.context) or self.context
if not checkPermission(act.permission, ctx):
continue
result.append(act)
return result
@Lazy
def showObjectActions(self):
return not IUnauthenticatedPrincipal.providedBy(self.request.principal)

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2012 Helmut Merz helmutm@cy55.de
# Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -51,17 +51,18 @@ from cybertools.typology.interfaces import IType, ITypeManager
from cybertools.util.jeep import Jeep
from cybertools.xedit.browser import ExternalEditorView
from loops.browser.action import actions, DialogAction
from loops.browser.common import BaseView
from loops.browser.concept import ConceptView
from loops.common import adapted, AdapterBase, baseObject
from loops.i18n.browser import i18n_macros, LanguageInfo
from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode
from loops.interfaces import IViewConfiguratorSchema
from loops.resource import MediaAsset
from loops import util
from loops.util import _
from loops.browser.common import BaseView
from loops.browser.concept import ConceptView
from loops.organize.interfaces import IPresence
from loops.organize.tracking import access
from loops.resource import MediaAsset
from loops.security.common import canWriteObject
from loops import util
from loops.util import _
from loops.versioning.util import getVersion
@ -149,13 +150,15 @@ class NodeView(BaseView):
priority=20)
cm.register('portlet_left', 'navigation', title='Navigation',
subMacro=node_macros.macros['menu'])
if canWrite(self.context, 'title') or (
if canWriteObject(self.context) or (
# TODO: is this useful in any case?
self.virtualTargetObject is not None and
canWrite(self.virtualTargetObject, 'title')):
canWriteObject(self.virtualTargetObject)):
# check if there are any available actions;
# store list of actions in macro object (evaluate only once)
actions = [act for act in self.getActions('portlet') if act.condition]
actions = [act for act in self.getAllowedActions('portlet',
target=self.virtualTarget)
if act.condition]
if actions:
cm.register('portlet_right', 'actions', title=_(u'Actions'),
subMacro=node_macros.macros['actions'],
@ -534,7 +537,7 @@ class NodeView(BaseView):
return self.makeTargetUrl(self.url, util.getUidForObject(target),
target.title)
def getActions(self, category='object', target=None):
def getActions(self, category='object', page=None, target=None):
actions = []
#self.registerDojo()
self.registerDojoFormAll()
@ -557,9 +560,11 @@ class NodeView(BaseView):
description='Open concept map editor in new window',
url=cmeUrl, target=target))
if self.checkAction('create_resource', 'portlet', target):
actions.append(DialogAction(self, title='Create Resource...',
actions.append(DialogAction(self, name='create_resource',
title='Create Resource...',
description='Create a new resource object.',
page=self, target=target))
page=self, target=target,
permission='zope.ManageContent'))
return actions
actions = dict(portlet=getPortletActions)

View file

@ -293,7 +293,8 @@
<metal:actions define-macro="object_actions">
<div id="object-actions" class="object-actions"
tal:define="target nocall:target|nothing;">
<tal:actions repeat="action python:view.getActions('object', target=target)">
<tal:actions repeat="action python:
view.getAllowedActions('object', target=target)">
<metal:action use-macro="action/macro" />
</tal:actions>
</div>
@ -322,7 +323,7 @@
<div><a href="logout.html?nextURL=login.html"
tal:attributes="href string:logout.html?nextURL=${view/menu/url}"
i18n:translate="">Log out</a></div>
<tal:actions repeat="action python:view.getActions('personal')">
<tal:actions repeat="action python:view.getAllowedActions('personal')">
<metal:action use-macro="action/macro" />
</tal:actions>
</metal:actions>

View file

@ -52,6 +52,7 @@ actions.register('createBlogPost', 'portlet', DialogAction,
fixedType=True,
innerForm='inner_concept_form.html',
prerequisites=['registerDojoDateWidget'], # +'registerDojoTextWidget'?
permission='loops.AssignAsParent',
)

View file

@ -50,6 +50,7 @@ actions.register('createTopic', 'portlet', DialogAction,
typeToken='.loops/concepts/topic',
fixedType=True,
innerForm='inner_concept_form.html',
permission='loops.AssignAsParent',
)
actions.register('editTopic', 'portlet', DialogAction,
@ -57,6 +58,7 @@ actions.register('editTopic', 'portlet', DialogAction,
description=_(u'Modify topic.'),
viewName='edit_concept.html',
dialogName='editTopic',
permission='zope.ManageContent',
)
actions.register('createQualification', 'portlet', DialogAction,
@ -66,6 +68,7 @@ actions.register('createQualification', 'portlet', DialogAction,
dialogName='createQualification',
prerequisites=['registerDojoDateWidget', 'registerDojoNumberWidget',
'registerDojoTextarea'],
permission='loops.AssignAsParent',
)

View file

@ -56,6 +56,7 @@ actions.register('createEvent', 'portlet', DialogAction,
typeToken='.loops/concepts/event',
fixedType=True,
prerequisites=['registerDojoDateWidget'],
permission='loops.AssignAsParent',
)
actions.register('editEvent', 'portlet', DialogAction,

View file

@ -27,7 +27,7 @@
<zope:adapter
factory="loops.organize.stateful.base.SimplePublishable"
name="simple_publishing" trusted="True" />
name="simple_publishing" />
<zope:class class="loops.organize.stateful.base.SimplePublishable">
<require permission="zope.View"
interface="cybertools.stateful.interfaces.IStateful" />
@ -41,7 +41,7 @@
<zope:adapter
factory="loops.organize.stateful.task.StatefulTask"
name="task_states" trusted="True" />
name="task_states" />
<zope:class class="loops.organize.stateful.task.StatefulTask">
<require permission="zope.View"
interface="cybertools.stateful.interfaces.IStateful" />
@ -55,7 +55,7 @@
<zope:adapter
factory="loops.organize.stateful.task.PublishableTask"
name="publishable_task" trusted="True" />
name="publishable_task" />
<zope:class class="loops.organize.stateful.task.PublishableTask">
<require permission="zope.View"
interface="cybertools.stateful.interfaces.IStateful" />
@ -69,7 +69,7 @@
<zope:adapter
factory="loops.organize.stateful.quality.ClassificationQualityCheckable"
name="classification_quality" trusted="True" />
name="classification_quality" />
<zope:class class="loops.organize.stateful.quality.ClassificationQualityCheckable">
<require permission="zope.View"
interface="cybertools.stateful.interfaces.IStateful" />

View file

@ -75,7 +75,7 @@ def canListObject(obj, noCheck=False):
return canAccess(obj, 'title')
def canWriteObject(obj):
return canWrite(obj, 'title')
return canWrite(obj, 'title') or canAssignAsParent(obj)
def canEditRestricted(obj):
return checkPermission('loops.EditRestricted', obj)

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# Copyright (c) 2013 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -18,8 +18,6 @@
"""
Interfaces for loops security management.
$Id$
"""
from zope.interface import Interface, Attribute
@ -35,6 +33,10 @@ class ISecuritySetter(Interface):
context object.
"""
def setStateSecurity():
""" Set the security according to the state(s) of the object.
"""
def setDefaultRolePermissions():
""" Set some default role permission assignments (grants) on the
context object.

View file

@ -123,7 +123,6 @@ class LoopsObjectSecuritySetter(BaseSecuritySetter):
rpm = self.rolePermissionManager
for p, r, s in rpm.getRolesAndPermissions():
setRolePermission(rpm, p, r, Unset)
self.setStateSecurity()
def setStateSecurity(self):
statesDefs = (self.globalOptions('organize.stateful.concept', []) +
@ -151,6 +150,7 @@ class LoopsObjectSecuritySetter(BaseSecuritySetter):
settings[(p, r)] = s
self.setDefaultRolePermissions()
self.setRolePermissions(settings)
self.setStateSecurity()
def setRolePermissions(self, settings):
for (p, r), s in settings.items():