Alfresco coding best practice : how to safely call java services ?

January 6, 2010

In alfresco there are two ways of calling a repository service: e.g., lower case “nodeService” and upper case “NodeService” etc (similarly for rest of the available repository services).

To be able to use a service bean in your custom code, you usually use a spring configuration file to inject it dynamically in your class (using spring IOC).
So in your spring configuration file, for your class “com.mycompany.MyBeanClass”, you can choose to inject the Alfresco service (nodeService below) using the “nodeService” (lower case) or “NodeService”(upper case) naming convention:

   <bean id="myBean" >
      <property name="nodeService">
          <ref bean="nodeService" />
      </property>
   </bean>

or:

   <bean id="myBean" >
      <property name="nodeService">
          <ref bean="NodeService" />
      </property>
   </bean>

As a coding best practice it is highly recommended to use services with upper case “NodeService” etc., because this “nodeService” bypasses security check, transaction check, etc.
The technical explanation is that Alfresco use AOP (Aspect-Oriented Programming) to expose services either as standard spring bean, or as “AOP proxies”.
To better understand that, let’s have a look at these 2 files, which contains a reference to the ContentService bean:

/webapps/alfresco/WEB-INF/classes/alfresco
public-services-context.xml
content-services-context.xml

The public-services-context.xml contains a reference to the “ContentService” (lower case) which is actually a proxy object (AOP proxy) for the  “contentService” (lower case) referenced in content-services-context.xml:

public-services-context.xml

    <bean id="ContentService">
        <property name="proxyInterfaces">
            <value>org.alfresco.service.cmr.repository.ContentService</value>
        </property>
        <property name="target">
            <ref bean="contentService"/>
        </property>
        <property name="interceptorNames">
            <list>
                <idref local="ContentService_transaction"/>
                <idref local="AuditMethodInterceptor"/>
                <idref local="exceptionTranslator"/>
                <idref bean="mlContentInterceptor"/>
                <idref bean="ContentService_security"/>
            </list>
        </property>
    </bean>
 

content-services-context.xml

   <!-- Abstract bean definition defining base definition for content service -->
   <bean id="baseContentService" abstract="true" init-method="init">
      <property name="retryingTransactionHelper">
          <ref bean="retryingTransactionHelper"/>
      </property>
      <property name="dictionaryService">
          <ref bean="dictionaryService" />
      </property>
      <property name="nodeService">
          <ref bean="nodeService" />
      </property>
      <property name="transformerRegistry">
          <ref bean="contentTransformerRegistry" />
      </property>
      <property name="policyComponent">
          <ref bean="policyComponent" />
      </property>
      <property name="avmService">
          <ref bean="avmService"/>
      </property>
      <property name="imageMagickContentTransformer">
         <ref bean="transformer.ImageMagick" />
      </property>
      <property name="eagerContentStoreCleaner" >
         <ref bean="eagerContentStoreCleaner" />
      </property>
   </bean>
  
   <bean id="contentService" parent="baseContentService">
      <property name="store">
          <ref bean="fileContentStore" />
      </property>
   </bean>

In the public-services-context.xml, we can see a list of interceptors, which force the execution of security check (ContentService_security) or audit action (AuditMethodInterceptor) or even transaction context (ContentService_transaction).

        <property name="interceptorNames">
            <list>
                <idref local="ContentService_transaction"/>
                <idref local="AuditMethodInterceptor"/>
                <idref local="exceptionTranslator"/>
                <idref bean="mlContentInterceptor"/>
                <idref bean="ContentService_security"/>
            </list>
        </property>

So when someone call directly the the  “contentService” (lower case), all these check are bypassed. This can leads to security issue (for instance) because the Alfresco system in this case does not evaluate the security before running the corresponding service operation.
So the conclusion is in almost all the cases it is best to use upper case services, especially in places where we know that there is a possibility of “no proper security check”. The only case where minor case service might be use safely is likely to be “admin treatment” (like batch), when one knows that we do not need to check security for such technical operation. But keep in mind that transaction are also bypassed, so I think they should be explicitly managed at the service call level if needed.


Think about Share as an Extranet web app

March 12, 2009

It’s interesting to notice that Alfresco Share has been primarily designed as an Extranet web application, built on top of the Alfresco Document Repository.

The more I study Share, the more I understand that it has been designed as a front-office for Alfresco DM.

In the current version of Share, most of the “admin” task are managed through the Alfresco DM web UI (JSF client):

For instance, if you want to setup notification rule for Share Site “testsite”, you have to connect to Alfresco DM, and then go to “Company Home / Site / testsite”, and then apply rules on the sub-spaces which corresponds to the Share pages (like Blog, wiki, etc).

Each “page” of the Share site corresponds to an Alfresco Space. So each time a Site is created, the following spaces are automatically created in the DM:

– blog
– calendar
– discussions
– documentLibrary
– links
– wiki

So you can choose to apply notification only for the Space “Calendar”. This is really as simple as applying any rule on Space in the DM. (I think you could also transform all your Share Blog post in .pdf …to be tested).

Another example, is that when you create a new Site, Share leverage the Alfresco DM security and automatically creates User Group associated with your Site. You can see all these groups if you connect as admin in Alfresco DM (and go to the Group management interface). Assuming your site is named “testsite”, then you will see the following Group sctructure:

site_testsite
 site_testsite_SiteManager
 site_testsite_SiteCollaborator
 site_testsite_SiteContributor
 site_testsite_SiteConsumer

Then, when you invite a User in “testsite” (and give the consumer role), this will automatically add this User as a member of the corresponding group site_testsite_SiteConsumer.

 

So consider Share as the front-end, and Alfresco DM as the back-end, and this will help you to understand how these 2 web app are coupled together.