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 loops - Linked Objects for Organization and Processing Services
=============================================================== ===============================================================
($Id$)
The loops platform consists up of three basic types of objects: The loops platform consists up of three basic types of objects:
(1) concepts: simple interconnected objects usually representing (1) concepts: simple interconnected objects usually representing
@ -612,7 +610,7 @@ Actions
>>> view.controller = Controller(view, request) >>> view.controller = Controller(view, request)
>>> #view.setupController() >>> #view.setupController()
>>> actions = view.getActions('portlet') >>> actions = view.getAllowedActions('portlet')
>>> len(actions) >>> len(actions)
2 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 ask a view which view and edit actions it supports. We directly use the
target object's view here: target object's view here:
>>> actions = view.virtualTarget.getActions('object', page=view) >>> actions = view.virtualTarget.getAllowedActions('object', page=view)
>>> #actions[0].url >>> #actions[0].url
'http://127.0.0.1/loops/views/m1/m11/m111/.target23' '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 # 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 # 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', viewName='edit_object.html',
dialogName='edit', dialogName='edit',
prerequisites=['registerDojoEditor'], prerequisites=['registerDojoEditor'],
permission='zope.ManageContent',
) )
actions.register('edit_concept', 'portlet', DialogAction, actions.register('edit_concept', 'portlet', DialogAction,
@ -137,6 +138,7 @@ actions.register('edit_concept', 'portlet', DialogAction,
viewName='edit_concept.html', viewName='edit_concept.html',
dialogName='edit', dialogName='edit',
prerequisites=['registerDojoEditor'], prerequisites=['registerDojoEditor'],
permission='zope.ManageContent',
) )
actions.register('create_concept', 'portlet', DialogAction, actions.register('create_concept', 'portlet', DialogAction,
@ -146,6 +148,7 @@ actions.register('create_concept', 'portlet', DialogAction,
dialogName='createConcept', dialogName='createConcept',
qualifier='create_concept', qualifier='create_concept',
innerForm='inner_concept_form.html', innerForm='inner_concept_form.html',
permission='loops.AssignAsParent',
) )
actions.register('create_subtype', 'portlet', DialogAction, actions.register('create_subtype', 'portlet', DialogAction,
@ -155,4 +158,5 @@ actions.register('create_subtype', 'portlet', DialogAction,
dialogName='createConcept', dialogName='createConcept',
qualifier='subtype', qualifier='subtype',
innerForm='inner_concept_form.html', 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 # 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 # 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.publisher.interfaces.browser import IBrowserSkinType, IBrowserView
from zope import schema from zope import schema
from zope.schema.vocabulary import SimpleTerm 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.interfaces import ForbiddenAttribute, Unauthorized
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
from zope.traversing.browser import absoluteURL 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.interfaces import IResource, IView, INode, ITypeConcept
from loops.organize.tracking import access from loops.organize.tracking import access
from loops.resource import Resource from loops.resource import Resource
from loops.security.common import checkPermission
from loops.security.common import canAccessObject, canListObject, canWriteObject from loops.security.common import canAccessObject, canListObject, canWriteObject
from loops.type import ITypeConcept from loops.type import ITypeConcept
from loops import util from loops import util
@ -706,6 +707,16 @@ class BaseView(GenericView, I18NView):
""" """
return [] 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 @Lazy
def showObjectActions(self): def showObjectActions(self):
return not IUnauthenticatedPrincipal.providedBy(self.request.principal) 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 # 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 # 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.util.jeep import Jeep
from cybertools.xedit.browser import ExternalEditorView from cybertools.xedit.browser import ExternalEditorView
from loops.browser.action import actions, DialogAction 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.common import adapted, AdapterBase, baseObject
from loops.i18n.browser import i18n_macros, LanguageInfo from loops.i18n.browser import i18n_macros, LanguageInfo
from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode
from loops.interfaces import IViewConfiguratorSchema 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.interfaces import IPresence
from loops.organize.tracking import access 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 from loops.versioning.util import getVersion
@ -149,13 +150,15 @@ class NodeView(BaseView):
priority=20) priority=20)
cm.register('portlet_left', 'navigation', title='Navigation', cm.register('portlet_left', 'navigation', title='Navigation',
subMacro=node_macros.macros['menu']) subMacro=node_macros.macros['menu'])
if canWrite(self.context, 'title') or ( if canWriteObject(self.context) or (
# TODO: is this useful in any case? # TODO: is this useful in any case?
self.virtualTargetObject is not None and self.virtualTargetObject is not None and
canWrite(self.virtualTargetObject, 'title')): canWriteObject(self.virtualTargetObject)):
# check if there are any available actions; # check if there are any available actions;
# store list of actions in macro object (evaluate only once) # 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: if actions:
cm.register('portlet_right', 'actions', title=_(u'Actions'), cm.register('portlet_right', 'actions', title=_(u'Actions'),
subMacro=node_macros.macros['actions'], subMacro=node_macros.macros['actions'],
@ -534,7 +537,7 @@ class NodeView(BaseView):
return self.makeTargetUrl(self.url, util.getUidForObject(target), return self.makeTargetUrl(self.url, util.getUidForObject(target),
target.title) target.title)
def getActions(self, category='object', target=None): def getActions(self, category='object', page=None, target=None):
actions = [] actions = []
#self.registerDojo() #self.registerDojo()
self.registerDojoFormAll() self.registerDojoFormAll()
@ -557,9 +560,11 @@ class NodeView(BaseView):
description='Open concept map editor in new window', description='Open concept map editor in new window',
url=cmeUrl, target=target)) url=cmeUrl, target=target))
if self.checkAction('create_resource', 'portlet', 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.', description='Create a new resource object.',
page=self, target=target)) page=self, target=target,
permission='zope.ManageContent'))
return actions return actions
actions = dict(portlet=getPortletActions) actions = dict(portlet=getPortletActions)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -75,7 +75,7 @@ def canListObject(obj, noCheck=False):
return canAccess(obj, 'title') return canAccess(obj, 'title')
def canWriteObject(obj): def canWriteObject(obj):
return canWrite(obj, 'title') return canWrite(obj, 'title') or canAssignAsParent(obj)
def canEditRestricted(obj): def canEditRestricted(obj):
return checkPermission('loops.EditRestricted', 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 # 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 # it under the terms of the GNU General Public License as published by
@ -18,8 +18,6 @@
""" """
Interfaces for loops security management. Interfaces for loops security management.
$Id$
""" """
from zope.interface import Interface, Attribute from zope.interface import Interface, Attribute
@ -35,6 +33,10 @@ class ISecuritySetter(Interface):
context object. context object.
""" """
def setStateSecurity():
""" Set the security according to the state(s) of the object.
"""
def setDefaultRolePermissions(): def setDefaultRolePermissions():
""" Set some default role permission assignments (grants) on the """ Set some default role permission assignments (grants) on the
context object. context object.

View file

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