Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / Tips / Security and Workflow

Security and Workflow

Plone 權限與工作流程緊密相關,牽涉的項目包括角色、群組、狀態等。

預設的工作流程稱為 Simple Publication Workflow,從 /@@content-controlpanel 網址可以存取管理介面,針對各別型別指定適合的工作流程。常見的需求,是把公開網站改成內部網站

新增使用者並指定權限

  • Administrators (管理者) 具備系統最高權限
  • Site Administrators (網站管理員) 具備編輯所有內容的權限
  • Reviewers (審核者) 可以被指派負責特定目錄裡的內容發佈

Permissions

Plone 的權限使用 Zope 的安全機制為基礎,組成元素包括 permission、role、group、workflow、state、transition 等,常見像 'Manage portal' 或 'View' 之類的設定值,被稱為 CMF Core Permission,完整定義在 Products/CMFCore/permissions.py 檔案裡。

Unauthorized Exceptions

Always ask for permission, never for role. If you ask for role, you may as well put app logic in TALES or just use PHP. Also, objects' methods are protected by permissions, not roles.

透過 permission 的 title 或 id 來查詢

plone.app.workflowmanager 搭配 Graphviz 視覺化管理 collective.wtf: spreadsheet

控制 Default View, Default Layout 的設定權限

zope2.View vs zope.View zope.Public

DocumentViewer

Delete Item in Folder Instead of Type

Edit Permission for Dexterity

manage_pasteObjects protected with "Modify portal content"

@security.public

permissions.zcml 從 plone.app.controlpanel 搬到 Products.CMFPlone

Change Portal Events 權限不再需要

Show Toolbar

Roles

指定範例 manage_permission 自製角色 getRolesInContext

"Show as" Option for a View under a specific Role: collective.powertoken.view

Restrict Transition on Multiple Roles

pyramid_localroles zopyx.plone.cassandra

Sharing

Sharing Tab 提供角色權限的管理介面,可以勾選 Can add、Can edit、Can view、Can review 的設定值,它們分別對應到 Contributor、Editor、Reader、Reviewer 角色,比對模組裡的角色設定後,來決定使用者是否具備權限,當然,除了這些基本的角色外,我們也可以新增角色權限,利用 GenericSetup 的 sharing.xml 檔案來設定。

Reader可以看到 private 狀態的內容,但不能修改,如果想讓某人審核一篇尚未發佈的文章,就該指定 Reader 角色。Contributor可以新增文件、使用版本管理、檢視尚未發佈的文章,如果想讓某人可以建立文件,卻不會修改到別人的文章,就該指定 Contributor 角色。Editor能夠修改,但不能新增文件,同時可管理文件的屬性值,並執行提交動作。Reviewer和 Editor 互斥,可以執行發佈動作,或退回給原作者。Manager可以做所有事,包括進入 ZMI 介面。Owner不論何時,都可以修改自己的文章? 這個待確認

範例:採用 example.conference 的 workflow 時,未登入者無法檢示 draft 狀態的內容。

Custom Permissions Dexterity Advanced permissions

隱藏目錄裡的檔案和圖檔,預設沒有套用工作流程,顯示條件就由 Container View Permission 的 acquire 狀況來決定。

針對 4.2.4 分析 openlogic on collaboaration

Workflow UI Discussion Sharing: Removing Acquisition

在 Sharing 裡,Can Read 影響的是 Reader Role 的權限

Can Add == Contributor Role

collective.subtractiveworkflow: two workflows for one content type

網頁方式建置工作流程的範例,使用工具包括 plone.app.workflowmanager、PloneFormGen、collective.pfg.dexterity。

bda.plone.wfintranet 範例

Authentication from Multiple Sources

http://blog.keul.it/2011/09/plone-security-and-workflows-when-rely.html

http://blog.keul.it/2011/10/plone-security-and-workflows-learn-how.html

Workflow Policy Support (CMFPlacefulWorkflow) vs portal_controlpanel

AccessControl

self having no attribute 'access', Acquisition jumps back to the os module which has an attribute being osaccess

Create Group to Manage Users

Custom Workflow is_allowed_state_change()

http://collective-docs.readthedocs.org/en/latest/sessions/login.html

Toolbar

http://www.it-spir.it/artikel/customize-plone-personal-tool-bar-style

取消顯示

collective.blog.portlets example

Programmatically Change State

from Products.CMFCore.utils import getToolByName
wftool = getToolByName(site, 'portal_workflow')
wftool.doActionFor(obj, 'publish')
obj.reindexObject()

Remove state menu from edit-bar

Reference Field for Another Type without Access Permission

<dtml-if expr="hasManagerPermission() !=1">
  /*This hides the image option in the Add New menu if you are not a manager*/
a#image.contenttype-image span.subMenuTitle
{
  display:none;
}

</dtml-if>

Acquire

Multiple Sites Effected by Acquisition

put setDefaultRoles in init.py outside of any function, so that it runs early during Zope startup

Workflow History

http://plone.org/documentation/manual/theme-reference/elements/visibleelements/plone.belowcontentbody.workflowhistory

Multiple Workflows

Multiple Workflows for Different Groups: Workflow Stacked

利用 CMF Placeful Workflow 可以讓目錄擁有各自的工作流程這個模組已內建安裝,只要啟用後,在工作狀態的下拉選單,可以看到「Policy...」選項。

讓 CMFWorkflow 針對 interface 客製化

Anonymous Adding Types

portal_action and workflow permission

Login with Username and Password

Role Drops to Anonymous When Adding Items with Custom Workflow

Masquerading As Another User In a Zope 3 Browser View

可能有兩層的設定:目錄要有 View, Add portal content, Access content information, MyPackage: Add MyType 型別要有 View, Modify portal content, Access content information

Workflow Script

利用 context.fieldname 來存取欄位值

Auto-Trigger

University of Wisconsin Oshkosh Workflow Training 範例: 申請表點選送出按鈕,狀態立即變成待審。

<transition transition_id="auto_to_private"
  title="Members only"
  new_state="private"
  trigger="AUTOMATIC"
  before_script=""
  after_script="">
  <guard>
    <guard-expression>not:object/@@netimpact-utils/is_contact_publishable</guard-expression>
  </guard>
</transition>

再搭配 Transition Trigger 程式碼

@grok.subscribe(IContact, IObjectModifiedEvent)
def trigger_contact_workflow(contact, event):
    wtool = getToolByName(contact, 'portal_workflow')
    wtool.doActionFor(contact, 'autotrigger')

Cross Site Request Forgery

plone.protect: AJAX POST Requests _authenticator=<Some token>

取消保護: collective.dancing plone4.csrffixes buildout.cfg Workflow Script

# buildout.cfg
environment-vars = PLONE_CSRF_DISABLED true

iframe 內容使用 https 或 http 會有不同影響。

plone.schemaeditor example

Guard

當 private 到 pending 時,啟用 SimplePublicationWorkflow

Miscellaneous

系統會信任來自檔案系統的程式碼,對於 Through The Web 的程式碼,會限制它們的功能或進行額外的權限檢查

getStatusOf

http://stackoverflow.com/questions/9667750/plone-4-restricting-published-content

cmf.ModifyPortalContent vs plone.app.iterate.permissions.CheckoutPermission

http://nathanvangheem.com/news/notes-on-a-more-secure-plone-deployment

像 intranet_workflow 並非使用 published 狀態名稱,或是沒有套用 workflow 的型別,可能造成模組無法正常運作

Local Role

hardcoded review state for anonymous users

Visitor Search Disabled, Only Member Can Search

Anonymous User Search Content

Support for Site Administrator Role: Products.Poi

Drupal Comarison

Member Basics

http://collective-docs.readthedocs.org/en/latest/members/member_basics.html

Read-only Public Site + Rewrite Login URLs

Basic Authentication

如果 Basic Auth Header 存在,就不會使用 Cookie 認證功能。

PAS

Get Started Slide

http://sharbas.blogspot.tw/2008/10/optional-auto-login-in-plone3.html auth_cookie_length

Password Hash

SSHA

Authentication MySQL

src_users = app.mysite.acl_users
dict(src_users._user_passwords)
{'jsmith': '{SSHA}jC/xX1xJ1eFn86HBdLGv)vciSNppMwWiRpCr'}
dict(src_user._userid_to_login)
{'jsmith': 'jsmith'}

plone.belowcontentbody.workflowhistory

http://stackoverflow.com/questions/14398585/how-to-cleanly-remove-a-plone-custom-permission

Image Folder 設定成 Intranet 狀態,但希望 Newsletter 若包含圖檔連結時,讀者是看得到內容的。可能方式之一,是把圖檔實際加進 Newsletter 來顯示,另外較麻煩的方式,是另為圖檔連結設定 View 來建立讀取權限。

Local Roles

在根目錄指定 Local Role 會影響效能

Delegating Plone managing users and local roles on content

Join / Register Form Attack

URL redirection after state or transition changed

Replace Local Roles on Users with Groups

http://plone.org/documentation/kb/adding-a-custom-permission-to-a-plone-2-5-product

想要取消 Local Role 的設定時,可在型別定義裡加上 flag 值:

class MyType(content.Container):
    """My content type
    """
    implements(IMyType)
    __ac_local_roles_block__ = True

Adding a custom permission to a product

Using rolemap.xml to create new Roles is easy, you can add predefined permissions to your new roles. But what if you want to define a new permission for your product? Use setDefaultRoles.

Note for Plone 4

Please note that this instruction target Plone 2.5 and Plone 3. In Plone 4, you can add permissions from ZCML directives.

Purpose

Let's say you are developing a product called MyProduct, in which you want to create a new permission called "MyProduct: MyPermission".

Prerequisities

This how-to is targeted at product developers.

Step by step

#1. Add a file called permissions.py.
from Products.CMFCore import permissions as CMFCorePermissions
from AccessControl.SecurityInfo import ModuleSecurityInfo
from Products.CMFCore.permissions import setDefaultRoles

security = ModuleSecurityInfo('MyProduct')

security.declarePublic('MyPermission')
MyPermission = 'MyProduct: MyPermission'
setDefaultRoles(MyPermission, ())

#2. Call this from your __init__.py.
# Initial permissions setup.
import permissions

The old way

There's been talk of Extensions/Install.py going away, so i'd advise against using this in your product.

Add this to your product's Extensions/Install.py:

from Products.CMFCore.permissions import setDefaultRoles

def install(self, reinstall=False):    
    setDefaultRoles('MyProduct: MyPermission', ())

Further information

Assigning permissions to a role using Generic Setup

If you also want to assign this permission to a role, 'Member' for example, the best way to go is to do it via Generic Setup in rolemap.xml:

<?xml version="1.0"?>
<rolemap>
  <roles>
    <role name="Member"/>
  </roles>
  <permissions>
    <permission name="MyProduct: MyPermission" acquire="False">
      <role name="Member"/>
    </permission>
  </permissions>
</rolemap>

As 'Member' is a default role, there is no need to include it in <roles />. If you want a custom role 'MyCustomRole' in your product, you could replace 'Member' by 'MyCustomRole' and be done.

Assigning permissions to roles the OLD way

Before Generic Setup, people used to assign permissions to roles in this way:

In Install.py:

from Products.CMFCore.permissions import setDefaultRoles

def install(self, reinstall=False):
    MY_PERMISSION = 'MyProduct: MyPermission'
    setDefaultRoles(MY_PERMISSION, ())
    PERMISSIONS = [
       (MY_PERMISSION, ['Member',], 0),
    ]
    for p in PERMISSIONS:
        self.manage_permission( p[0], p[1], p[2],)

Assigning permissions to roles the OLD way, several files

To cleanly seperate the code from the rest of your installation code, some people also spread it out over several files:

In permissions.py:

from Products.CMFCore.permissions import setDefaultRoles
from AccessControl import ModuleSecurityInfo
security = ModuleSecurityInfo('Products.MyProduct.permissions')
security.declarePublic('MY_PERMISSION')
MY_PERMISSION = 'MyProduct: MyPermission'
setDefaultRoles(MY_PERMISSION, ())
In AppConfig.py:

from Products.MyProduct.permissions import MY_PERMISSION
PERMISSIONS = [
   (MY_PERMISSION, 0, ['Member',]),
]
In Extensions/AppInstall.py:

from StringIO import StringIO
from Products.MyProduct.AppConfig import PERMISSIONS
def install(self):
    out = StringIO()
    for p in PERMISSIONS:
        self.manage_permission( p[0], p[1], p[2],)
    return out.getvalue()
In Extensions/Install.py:

from Products.ExternalMethod.ExternalMethod import ExternalMethod
    # try to call a custom install method
    # in 'AppInstall.py' method 'install'
    try:
        install = ExternalMethod('temp', 'temp', PROJECTNAME+'.AppInstall', 'install')
    except NotFound:
        install = None