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);
      }
...

3 comments:

  1. Great post. I created a JIRA ticket for
    "Trying to add new roles after Share sites have already been created will likely result in errors being thrown" ID (ALF-8149).

    I lost a good part of a day before I understood the problem. This explained it to me in minutes.

    ReplyDelete
  2. This is a great tutorial, which I found very helpful in implementing Alfresco. I'd like to point out one typographical error, though, in the definition of groups and roles for invitations and permissions:

    group.ExternalCotributor=External Contributors

    should be

    group.ExternalContributor=External Contributors

    ReplyDelete
  3. Great and very helpful post, thanks !!!!

    ReplyDelete