Showing posts with label information architechture. Show all posts
Showing posts with label information architechture. Show all posts

Sunday, October 16, 2011

Plone, security and workflows: learn how to use "Acquire" option

This article is a new episode of my security and workflow common mistake using Plone.

In the first part, "Plone, security and workflows: when rely on Owner role is bad" I introduced a potential workflow problem, that however can be a real problem only in some kind of organizations.

Today I don't want to focus onto roles, but on the use (or missing use) of the "Acquire" check when designing workflows.
Don't know how to use this checkbox can lead to security problem, especially in conjunction with the View permission.

Is also important to be noted that this problem can be inside current workflows distributed using Plone. This depends on how you and your users think that Plone security must work to fill your needs.

What is the "Aquire" meaning?
You can see the "Acquire" check ("Acquire permission settings?") while defining the workflow permissions page, for every permission managed by the workflow.

A workflow state's permission setting
The simple meaning is "roles that behave this permission are the ones listed there, plus ones defined on the container of this content".

If the parent itself use a workflow and manage same permissions, it can also use Acquire check again, so acquiring other permissions for roles from it's own parent... and so on.
This continue until the Plone site (or sometimes, up again to Zope root object) where all permissions are defined in the "Security" tab.

Use the Acquire settings without flagging additional roles for a permission is ok: the meaning is "this workflow state don't want to manage this permission". Instead, another state of the same workflow will probably add or change something to the permission.
If every state of a workflow simply use the Acquire check without change something, the customization of this permission in the workflow is useless and can probably be removed.

Also: don't manage a permission for a workflow is still ok. You will never find a workflow that handle all permissions. Only manage the ones you care about (so, for workflow for custom content types, please remove the "Change portal events" permission!).

Out of the box security
More than one Plone standard workflows suffer of what we can define "a not perfect configuration". This do not mean that this is a Plone architectural problem: with Plone you can create great, perfectly working and safe, workflows! Just: the provided ones could be not perfect in some situations and for being able to fix them or create new ones, you need to understand why!
Also, if the workflow is ok or not, depends on how you and your customer think that Plone must work.

The problem is commonly associated with the View permission, however all analysis that follow are the same for every other permissions. We will refer to "Simple Publication Workflow", while will be the same for other like "Intranet/Extranet Workflow".
The "Simple Publication Workflow" is used from all Plone default contents, also by folders.

Let's take a look at the published state permissions setting.

How the default public state looks like
Look in detail at the Anonymous column. The View permission is given at the Anonymous role. And this seems good: you are explicitly saying "every content in this state will be visible by the anonymous". Always. Everywhere.

How can be this bad? The content is public, I want that Anonymous see it!

Let think this applied to folders: users (99% of times, in my experience) see the private and the published state like a room with a door:
  • A published folder is like a room with an open door. You can see inside the room itself, and other rooms that follow (if those rooms are open)
  • A private folder is like a closed door. You can't see inside it, and you don't care if other doors that follow are open or closed. You don't even know if there are other doors!
Users that move from published to private a main folder that contains a deep tree of subfolder and contents, commonly think that this action will hide all the tree!

You probably already get the point. An explicit check of the View permission for a role is like a magic crystal ball. You don't care of the door: you have the power too see inside.

Going back to Plone world: think about having a Plone folder (folder-1) with a document inside (document-1). If the document is published you can see it using your crystal ball. The review state (even if private) of the containing folder doesn't matter.
This is what is not always clear to users.
  • Users can reach http://yourcompany.com/
  • Users can't reach http://yourcompany.com/folder-1 (he will get Unauthorized)
  • Users can reach http://yourcompany.com/folder-1/document-1
During developer training that customers ask us, one of the argument is always how to customize workflows. Sometimes those customers have already installed a Plone site or intranet by their own so when I explain this, is not uncommon to see surprised faces.

O__o

Another bad attitude: customers never tested the workflow deeply and they think that their contents are is a secure review state just because a main folder is private.

How to fix this
As said above: this is not a Plone technological error. There's a way to get exactly what we want: a set of rooms with doors, with no crystal ball, secret passage or teleport bridge that can transport unwanted users inside the room.

The trick is: don't say that the document is visible explicitly, leave this responsibility to the container (to the room's door).

This is traduced in a workflow configuration that remove explicit permission for Anonymous but use the Acquire power.

A workflow state's permission setting
Note also an important thing: we don't give the check to Anonymous, but we now check permissions for all other roles that matter. We mean there: all roles that must see the content ignoring if the access door is open or closed.

In this way an anonymous user is able to see the content if he can enter the room (published container folder) and will be no more able to see anything when the door is closed (private container folder).

This is really simple!

When not talking of anonymous access
Somewhere above I said that also the "Intranet/Extranet Workflow" suffer the same configuration problem. Can we fix also this?

This kind of workflow is designed for intranets, but also provide some review state for public contents: it add the "internally published" concept to our Plone site.

Note that this workflow is designed to be used with "Intranet Workflow for Folders" associated with folderish content types.

When using those workflows, we are sometimes referring to the Member roles as we did for Anonymous above.

The default configuration of the "internally_published" state is as follow:

A workflow state's permission setting
The concept behind this state is "having a published state for intranet users" (so excluding Anonymous).
So we have the same problem for the explicit check of the View permission, this time for Member role.

We can fix this in the same way:

A workflow state's permission setting
We removed the explicit View permission for Member role while enabling the "Acquire".

This time the cure has some limit.

First problem: if we put an internally_published content in the site root, in our "fixed" version of the workflow we are exposing the content to anonymous access, while the original version protects it (know that you will never remove the View permission for Anonymous role from the site root or Plone will work in unpredictable way).

Another problem: take a look at states defined for "Intranet Workflow for Folders". It's composed by two states: private and internal. If we look at the internal description we read "Visible to all intranet users", so it want to be a "published folder for intranet users".

The private state has no View permission for Member role, while the internal state explicitly check it for site members.
As explained above, this mean that if we put an internal folder inside a private folder, site members will be able to skip the private state of the main container folder and access directly the inner ones and all contents inside.

If we fix this removing the explicit check for Member while activating the "Acquire" check we will have a similar problem to the first ones: anonymous users will be able to access the internal folder in the site root...

There is no perfect solution for this problem using only a good workflow configuration. We need to make some default configuration to the site structure, helping the workflow:
  • We fix all "internal published like" states of both workflows as described (using "Acquire")
  • We put all internal folder in one (or more) main private folder(s) in the site root
  • We give to all users (we can also use the "authenticated users" virtual group) the "can read" check (Reader role) from the sharing tab of this private folder
  • We remove from all contents/subfolders inside master private folder(s) the roles inheritance from the sharing tab, removing Reader power to all members
  • Inner subfolder and contents will live with normal worfklow security settings
With a configuration like this we can be sure that Anonymous users can't access our contents (as they all live in a private folder) while still be able to control the visibility of a subtree changing the main folder review state (relying on "Acquire").

Think about having a dedicated workflow for folders
Designing a workflow can often be design a set of workflows. This is common; the "Intranet Workflow for Folders" is a perfect example. To use it right, it's better to use also the workflow for folders.

I find often useful to create two different workflow for real contents and folders.
Here a common request I get: Reader role must be able to enter inside private area, but inside this being able to only published contents.

Obviously the workflow I will design will be a workflow that rely onto "Acquire".
While the private state for folders will give explicit View permission to Reader, the private state for contents won't give such power. Done!

Conclusion
Before load your new workflow in a production site or intranet, be sure of what workflow you give to contents... and test it.

A simple way for performing those tests is_ search contents using the site search feature.

Saturday, July 16, 2011

Hidden (great) secrets inside ATVocabularyManager

ATVocabularyManager is a well-know Plone product developed by BlueDynamics that make simple handling vocabulary values used by your contents directly inside Plone.
That's all: the power of the product is all in this first sentence.

Generic Setup Strike Back
In my personal experience I not commonly found projects that need a Plone user able to change vocabularies, but in the information architecture this is quite common (they call this "Controlled Vocabulary"). So I used ATVocabularyManager a couple of times in the past, but I never became habit to rely on it.

Another thing I don't liked was the unexistent Generic Setup integration.

Recently a customer asked us a new project with some new content types, with a lot of field with controlled vocabularies (with many values inside). Also he explicitly ask to be able to handle and change it in the future.

So I looked back to ATVocabularyManager, hoping that during this time something changed.

A New Hope
This time I note immediately that latest releases (1.5 branch for Plone 3.3, and 1.6 for Plone 4) give us something new.

First of all: for the first time I understand that the name prefix "AT" means obviously "Archetypes", but you can think it as "the way of controlling vocabulary is done using some archetypes contents". This mean that you can use it only if your vocabularies are inside archetypes content types? No!
You can also use it to handle whatever ZCML vocabulary you need (portlet? Dexterity?)

The other thing I found is that now we have Generic Setup integration. Great!

The Generic Setup integration
Right now the integration is not fully complete. Seems that export step is not there, but looking at the code I saw that the import step code (the most important!) is available. The product right now suffer only some missing of documentation.

How the import steps works? Instead of creating new vocabulary content types ("Simple Vocabulary", "Sorted Simple Vocabulary", ...), it is based on the "IMS VDEX Vocabulary File" content.

At first glance this can seem the less user friendly way and most obscure content type (and probably this is true) but going back to information architecture this is probably the best choice, because it's based on an XML international standard for handle vocabularies: the IMS VDEX.
Another good news: this format also support i18n (and also Plone)!

What I needed after this is simple: provide a VDEX compatible XML file. How? Let show a complete example.

How to add the GS support
First of all you need to provide a "vocabularies.xml file" to your profile directory.
The format of the file is as follow:

<?xml version="1.0"?>
<object name="portal_vocabularies" meta_type="ATVocabularyManager">
<object name="test.vdex" /> 
...
</object>

So you need to provide a reference to a vocabulary file for every vocabulary you need to import (.vdex of .xml file extensions are valid ones).

Where to put all vocabulary files? You need also to put at the same level a "vocabularies" directory. Inside this you simply need to put all files.

Now I will show the file format of our test.vdex file.

<vdex xmlns="http://www.imsglobal.org/xsd/imsvdex_v1p0"
orderSignificant="true">
  <vocabIdentifier>test-vocab</vocabIdentifier>
  <vocabName>
    <langstring language="en">A test vocabulary</langstring>
    <langstring language="it">Un vocabolario di test</langstring>
  </vocabName>
  <term>
    <termIdentifier>aaa</termIdentifier>
    <caption>
      <langstring language="en">A value</langstring>
      <langstring language="it">Un valore</langstring>
    </caption>
  </term>
  <term>
    <termIdentifier>bbb</termIdentifier>
    <caption>
      <langstring language="en">Another value</langstring>
      <langstring language="it">Un altro valore</langstring>
    </caption>
  </term>
</vdex>

That's all. We created a vocabulary with two entry inside (foo values are "aaa" and "bbb"). As say above, handle this directly from Plone is not very comfortable (simpler vocabulary type are easier to understand) however this is great for Generic Setup install step!

Internationalization note
A little bug on this approach. Seems that even if you plan to not provide an internationalization for you vocabularies (for example: you only need to provide your italian, spanish or something other translation) you still need to provide also the english one, or the vocabulary content inside ATVocabularyManager control panel will not show you the right title of the vocabulary (something like "unnamed vocabulary" instead of "Un vocabolario di test").
But you can use a trick and duplicate your locale specific translation also for english. Also, put english translation first.

Not very comfortable right now
If you still think it, you are right. Maybe that vdex is a well know standard for handle vocabulary, but build a vocabulary with this XML format can be not very simple.

For example, the customer give us a document (a MS Word attachment, obviously) with a set of lists of values. The easy way is to put all this in some CSV files, where columns are "italian translation" and "english translation".
But after that we need to convert this in the vdex format. How?

What I did is too look on the cheeseshop for a library that can convert a CSV in a VDEX file. What I find is vdexcsv!
Two funny thing about it:
  • It was released something like two hour before I performed that search!
  • The company behind this product is again BlueDynamics!
What this product does? You only need to easy_install it then you will be gifted with a new bash command: csv2vdex.

The documentation is clear: you need to provide a CSV file, some parameter, and you'll obtain your vdex file.

For the example above I used a CSV like this:

"key";"english";"italian"
"aaa";"A value";"Un valore"
"bbb";"Another value";"Un altro valore"

This was named test.csv.

Then I called the script in this way:
csv2vdex test-vocab 'A test vocabulary,Un vocabolario di test' test.csv test.vdex --languages en,it --startrow 1

One last step: the generated vdex file is perfect for all but the root node name. As in the example above you need to have the root node called vdex, but the script generate it as vocabulary. However after contacting Jens (the product creator, that also help me to reach this results) he was in agreement to change this in future releases of vdexcsv.
For now, simply rename the node!
EDIT (2011-08-22): the good guys released vdexcsv 1.1 that is now fully standard compliant, so no more needs to manually rename the node!