Monday, April 4, 2011

Alfresco Share Permissions/Roles -- Part II

Creating Custom Alfresco Permissions/Roles

In the previous blog we saw how we were able to fairly easily replace the Share Manage Permissions dialog with the Manage Permissions page used for the Repository button browser.  This allowed us to be able to assign at a much more granular level permissions to the folders and items that are stored within a Share site Document Library.

Now consider the scenario where we would like to be able to invite users to our site, and these users should be able to see and modify only a selected set of documents within the Document Library.  We want access to the site for the majority of users to be unrestricted.  This scenario doesn't work well with the standard Share site roles of Manager, Collaborator, Contributor and Consumer.

To invite a restricted user to the site, we still need to give them a role.  Even if we give this user the role of Site Consumer, they will be able to see much more content in the site than what we want them to see.

What I propose here to help solve this problem is to add new custom Share site roles.  Alfresco has a wiki page here that provides a good start for what needs to be done in creating a custom Share role.  After following the instructions there, which were targeted for version 3.2r, I ran into some issues, and I noted a number of other people in the Alfresco forums also had some issues with it.

What I describe here should work with a fresh Alfresco install.  Trying to add new roles after Share sites have already been created will likely result in errors being thrown.  The reason why this happens is that the appropriate group authorities will not exist and Share will report that as an error.  Once these new roles are created, Share expects them to exist for all sites.  The absence of these authorities for existing sites likely is  something that can be corrected by manually creating the correct authority objects in Alfresco, but we don't attempt to do that here.

Here we will create three new permissions sets/roles called External Consumer, External Contributor, and External Collaborator. The permissions for each of these roles are identical to those of the corresponding Site Consumer, Site Contributor and Site Collaborator that come standard with Share.  What will be different is how these permissions are applied to content in the Document Library.

To do that, we edit the file sitePermissionDefinitions.xml and replicate the lines for SiteContributor, SiteConsumer, and SiteCollaborator permissionGroups.  This file is then placed into the following directory:
tomcat/shared/classes/alfresco/extension/model/
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE permissions >

<permissions>
    
    <!-- Namespaces used in type references -->
    
   <namespaces>
      <namespace uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
      <namespace uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
      <namespace uri="http://www.alfresco.org/model/site/1.0" prefix="st"/>
   </namespaces>
   
   <!-- ============================================ -->
   <!-- Permissions specific to the wiki integration -->
   <!-- ============================================ -->
   
   <permissionSet type="st:site" expose="selected">
   
      <permissionGroup name="SiteManager" allowFullControl="true" expose="true" />
      
      <permissionGroup name="SiteCollaborator" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Collaborator" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="SiteContributor" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Contributor" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="SiteConsumer" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Consumer" type="cm:cmobject" />
      </permissionGroup>

      <permissionGroup name="ExternalCollaborator" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Collaborator" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="ExternalContributor" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Contributor" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="ExternalConsumer" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Consumer" type="cm:cmobject" />
      </permissionGroup>
      
   </permissionSet>

</permissions>
We need to alert Alfresco that this override file should be loaded on startup.  To do that, we create a new file with the path
tomcat/shared/classes/alfresco/extension/restricted-role-context.xml.
The contents of that file are as follows:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<!-- This file enables Alfresco Custom Site Roles.  It should be placed in shared/classes/extension -->

<beans>

    <bean id="siteService_permissionBootstrap" parent="permissionModelBootstrap">
     <property name="model" value="alfresco/extension/model/sitePermissionDefinitions.xml"/>
    </bean>

</beans>
Finally, there are a number of files where we add string properties that can be picked up so that the new role names display correctly within the Share UI.
First we create the file
tomcat/shared/classes/alfresco/web-extension/invitation-service-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
     
    <bean id="invitationResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
     <property name="resourceBundles">
      <list>
       <value>alfresco.web-extension.messages.invitation-service</value>
      </list>
     </property>
   </bean>

</beans>
And the associated property file:
tomcat/shared/classes/alfresco/web-extension/messages/invitation-service.properties:
invitation.invitesender.email.role.ExternalCollaborator=External Collaborator
invitation.invitesender.email.role.ExternalContributor=External Contributor
invitation.invitesender.email.role.ExternalConsumer=External Consumer
We add the following lines to the file:
tomcat/shared/classes/alfresco/web-extension/custom-slingshot-application-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
   <bean id="webscripts.resources" class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
      <property name="resourceBundles">
         <list>
             <value>alfresco.messages.common</value>
             <value>alfresco.messages.slingshot</value>
            <value>alfresco.web-extension.messages.slingshot</value>
         </list>
      </property>
   </bean>
</beans>
And the referenced new properties are in the file:
tomcat/shared/classes/alfresco/web-extension/messages/slingshot.properties
## Custom Site External Reviewer Roles
role.ExternalCollaborator=External Collaborator
role.ExternalContributor=External Contributor
role.ExternalConsumer=External Consumer
Next we copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/folder-details/folder-info.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/folder-details
and include these lines at the end of the file:
folder-info.role.ExternalCollaborator=External Collaborator
folder-info.role.ExternalConsumer=External Consumer
folder-info.role.ExternalContributor=External Contributor
Similarly copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-info.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-info.get.properties
and include these lines at the end:
## Customer External Review Role
document-info.role.ExternalCollaborator=External Collaborator
document-info.role.ExternalConsumer=External Consumer
document-info.role.ExternalContributor=External Contributor
Copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/invite/invitationlist.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/invite/invitationlist.get.properties
and include these lines at the end:
## External Groups and Roles for Site
group.ExternalCollaborator=External Collaborators
role.ExternalCollaborator=External Collaborators

group.ExternalConsumer=External Consumers
role.ExternalConsumer=External Consumers

group.ExternalCotributor=External Contributors
role.ExternalContributor=External Contributors

And finally, copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/modules/documentlibrary/permissions.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/modules/documentlibrary/permissions.get.properties
and include these lines at the end:
## External Groups and Roles for Site
group.ExternalCollaborator=External Collaborators
role.ExternalCollaborator=External Collaborator privileges

group.ExternalConsumer=External Consumers
role.ExternalConsumer=External Consumer privileges

group.ExternalCotributor=External Contributors
role.ExternalContributor=External Contributor privileges

External Site Roles in Action
Whew...
After doing that, we stop and restart the Alfresco server.  We can then log in and create a new Share site.

Immediately after creating the site, we can navigate to the root node for the site by using the Repository button in Share.  When we click on the Manage Permissions button for the new site, we can see that our new permissions sets (ExternalConsumer, ExternalCollaborator, and External Consumer) are included automatically and applied to this node.


At the Document Library level, we can create a folder structure where two top level folders are to be accessible only by the standard SiteConsumer, SiteCollaborator and SiteContributor.  And a third folder is available to standard users and is also open for viewing to External Reviewers.


The internal folder permissions look as follows:
With these settings, only internal reviewers will be able to see the content of this folder.

In the External Reviewer folder, we set the permissions as follows:
In this case, we can see that both external and standard internal reviewers are able to access this folder.
And if we navigate one Folder down in the hierarchy of the External Reviewer Folder, we can see that the inheritance of these permissions flow down.  One folder down, we see the permissions are set in the same way:
One last note.  In order for the user/group search capability to work correctly on this form, I found that the "Add User/Group" button on this page does not find Alfresco Share groups.  By default, search is performed for groups within the ALF.DEFAULT zone which does not include the Share zone groups.  In order to find our groups, the following Javascript file was changed (Note the changes in bold made to that file):
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/people-finder/authority-query.get.js
var getMappings = function()
{
   var mappings = [],
      authorityType = args.authorityType === null ? "all" : String(args.authorityType).toLowerCase();
   
   if (authorityType === "all" || authorityType == "user")
   {
      mappings.push(
      {
         type: MAPPING_TYPE.API,
         url: "/api/people?filter=" + encodeURIComponent(args.filter),
         rootObject: "people",
         fn: mapUser
      });
   }

   if (authorityType === "all" || authorityType === "group")
   {
      var url = "/api/groups?shortNameFilter=" + encodeURIComponent(args.filter);
//    if (args.zone !== "all")
// All authorities are to be found
      if (args.zone !== "all" && args.zone !== null)
      {
         url += "&zone=" + encodeURIComponent(args.zone === null ? "APP.DEFAULT" : args.zone);
      }
...

Friday, April 1, 2011

Alfresco Share Permissions/Roles -- Part I

Accessing Granular Permission Rights within Alfresco Share

People are sometimes surprised to find that although the Alfresco repository is architected to have very granular permissions, permissions assignments via the Share UI are based on Site roles.

Share Site Roles
The Share roles are as follows:



Site Consumer    Read
Site Contributor    Read, Upload
Site Collaborator    Read, Upload, Checkout, Edit 
Site Manager    Full Control


Interestingly, Alfresco did not include a Site Editor role for the Share UI, despite the fact that the Editor is a standard role used within the repository and is accessible in the Alfresco Explorer client. [Note the editor can change existing document but doesn't have the Upload/Create capability.]

Within the Alfresco Share client, assignment of permissions at the object level is possible using the Manage Permissions action dialog:  Here we can see that permissions are assigned to the standard Share site groups.  There is no way using this dialog to specify permissions for an individual.

The mix of using the same name, like 'Site Consumer' when referring to groups, privilege sets and roles also makes for some confusion on this dialog.



Role Assignments via the Share Repository Button
If permissions on objects can be enforced only by role, how can one restrict the viewing of some of the documents in the repository to only one or a few individuals?  It can be done from the Explorer client.  And, interestingly, it can also be accessed in Alfresco Share when browsing the Alfresco Repository, a feature which has been made available in Share.

When browsing the repository, a different page is displayed for setting permissions than the dialog we saw above which is used when browsing within a site Document Library.  I'm not sure why Alfresco developers decided on this particular discrepancy in the design.

So setting permissions at a much more granular level  is available within Share out of the box, but its location is a bit problematic because it isn't obvious that it is available.

Changing the Manage Permissions Action
To change the behavior of the Manage Permissions action to be the same as that available from the Share Repository button is not hard.  To do that we need to modify three files.  First we make folders in the tomcat/shared folder to hold the Share files that we will change.

In the document-details folder, we copy over the file document-actions.get.config.xml.
In the documentlibrary folder, we copy over the file documentlist.get.config.xml.
In the folder-details folder, we copy over the file folder-actions.get.config.xml.

In these files, we then edit the following line:

<action id="onActionManagePermissions" label="actions.document.manage-permissions" permission="permissions" type="action-link"></action>>

This line is changed to the following:

<action type="simple-link" id="onActionManagePermissions" permission="permissions" href="{managePermissionsUrl}" label="actions.folder.manage-permissions" />

This same line needs to change once in each of
document-actions.get.config.xml and folder-actions.get.config.xml files
and twice in the file documentlist.get.config.xml.

After adding these files, we stop and start the server.

Testing out the Manage Permissions Changes
Consider the case of a manager who has uploaded a new document, a document that he wants only Susan, a user with role Site Consumer, to be able to see it.  (Although note that site managers will always be able to see all site document regardless of permission settings.)

After creating the document, click on the document action Manage Permissions.  We'll see a screen that looks like the following:
Because, via inheritance of permissions in Share, all members of the Share site currently have access to see this document. Let's change the access permissions by doing the following:
  • click on Inherit Permissions to turn off that inheritance, and then 
  • click  on Add User/Group and selecting Susan as the only user with permissions to see this document
  • click on the save button to save the new permission settings
After doing that, the Manage Permissions screen shows the following.

Login as Mary
Now we can log out as the site manager and log in as as Mary, a Site Collaborator.  Mary should have no permission to see this document.

Mary can navigate into the document library for the site and she does not see the document restricted to Susan.

One glitch does show up here.  In the "Site Activities" Dashlet of the site, the document still shows up.  See the top document in this list, BGESWFViewer.png below:

This seems to be a bug with this Alfresco dashlet to have allowed a restricted document's name to be seen by someone without permission.  But, other than that, this seems to work pretty well.

And when Mary clicks on the link to the document that she sees in this dashlet, she finds that she is restricted from accessing it:


Login as Susan, Site Consumer
When logging in as Susan, in the Dashboard for the site, we see references to the document in both the "Recently Modified Document" dashlet and also the "Site Activities" dashlet.  But this is as it should be since Susan has rights to see this document.


On navigation into the Document Library, Susan is able to successfully open the document.  She can open the document details page.  If you notice when we assigned permissions to Susan above, we gave her permission as "Site Collaborator".  In that case, Susan is able to edit the metadata for it.